From b090d5898540609a1dd936884ebbb4a4dff52d81 Mon Sep 17 00:00:00 2001 From: NSoiffer Date: Fri, 27 Sep 2024 15:28:30 -0700 Subject: [PATCH 001/120] Initial commit for French braille. 24 test passes, 3 test failures --- Rules/Braille/French/French_Rules.yaml | 937 +++++++++++++++++++++ Rules/Braille/French/definitions.yaml | 2 + Rules/Braille/French/unicode-full.yaml | 1027 ++++++++++++++++++++++++ Rules/Braille/French/unicode.yaml | 317 ++++++++ src/braille.rs | 190 ++++- tests/braille/French/chemistry.rs | 47 ++ tests/braille/French/french.rs | 138 ++++ 7 files changed, 2648 insertions(+), 10 deletions(-) create mode 100644 Rules/Braille/French/French_Rules.yaml create mode 100644 Rules/Braille/French/definitions.yaml create mode 100644 Rules/Braille/French/unicode-full.yaml create mode 100644 Rules/Braille/French/unicode.yaml create mode 100644 tests/braille/French/chemistry.rs create mode 100644 tests/braille/French/french.rs diff --git a/Rules/Braille/French/French_Rules.yaml b/Rules/Braille/French/French_Rules.yaml new file mode 100644 index 000000000..3dad1abe3 --- /dev/null +++ b/Rules/Braille/French/French_Rules.yaml @@ -0,0 +1,937 @@ +--- +- + name: whitespace-omission + tag: "!*" + match: "not(self::m:math) and not($MatchingWhitespace) and (@data-previous-space-width >= 0.25 or @data-following-space-width >= 0.25)" + replace: + - with: + variables: [MatchingWhitespace: "true()"] + replace: + - test: + - if: "(@class='MathML-unit' or BaseNode(.)[@class='MathML-unit']) and @data-previous-space-width >= 0.25" # BANA 5.3(a) + then: [t: "𝐖"] + - else_if: "@data-previous-space-width > 1.1" + then: [t: "⠬"] + - else_if: "@data-previous-space-width >= 0.25" # thickspace + then: [t: "W"] + - x: "." + - test: + - if: "(@class='MathML-unit' or BaseNode(.)[@class='MathML-unit']) and @data-following-space-width >= 0.25" # BANA 5.3(a) + then: [t: "𝐖"] + - else_if: "@data-following-space-width > 1.1" + then: [t: "⠬"] + - else_if: "@data-following-space-width >= 0.25" # thickspace + then: [t: "W"] + +- + name: omission-intent + tag: "!*" + match: "contains(@intent, ':blank')" + replace: + - t: "⠬" + +- + name: unicode-override + tag: "*" + match: "@data-unicode" + replace: + - x: "@data-unicode" + +- + name: default + tag: msqrt + match: "." + replace: + - t: "⠜" + - test: + if: "NeedsToBeGrouped(*[1], 'French', true())" + then: + - t: "⠰" + - x: "*[1]" + - t: "⠆" + else: + - x: "*[1]" + +- + name: default + tag: mroot + match: "." + replace: + - t: "⠈" + - test: + if: "NeedsToBeGrouped(*[2], 'French', true())" + then: + - t: "⠰" + - x: "*[2]" + - t: "⠆" + else: + - x: "*[2]" + - t: "⠜" + - test: + if: "NeedsToBeGrouped(*[1], 'French', true())" + then: + - t: "⠰" + - x: "*[1]" + - t: "⠆" + else: + - x: "*[1]" + + +# Fraction rules + # The fraction part of a mixed number is always simple + # They also differ because a two character sequence is needed to bracket the fraction + # The invisible plus should produce an empty string + +- + name: common-fraction-mixed-number + tag: mfrac + match: + - "preceding-sibling::*[1][self::m:mo][text()='\u2064'] and" # preceding element is invisible plus + - "*[1][self::m:mn][not(contains(., '.'))] and" + - "*[2][self::m:mn][not(contains(., '.'))]" + replace: + - t: "#" # signal end of previous numeric mode + - x: "*[1]" + - t: "N⠌" + - x: "*[2]" + +- + name: default + tag: mfrac + match: "." + replace: + - test: + if: "DEBUG(NeedsToBeGrouped(DEBUG(*[1]), 'French', true()))" + then: + - t: "⠰" + - x: "*[1]" + - t: "⠆" + else: + - x: "*[1]" + - t: "⠌" + - test: + if: "NeedsToBeGrouped(*[2], 'French', true())" + then: + - t: "⠰" + - x: "*[2]" + - t: "⠆" + else: + - x: "*[2]" + +- + #GTM 14.3.3 (not sure what else vertical juxtaposition applies to) + name: binomial-frac + tag: mrow + match: "IsBracketed(., '(', ')') and *[2][self::m:mfrac][@linethickness=0]" + replace: + - x: "*[1]" + - x: "*[2]/*[1]" + - t: "⠰⠻" + - x: "*[2]/*[2]" + - x: "*[3]" + +- name: binomial-table + tag: mrow + match: "IsBracketed(., '(', ')') and *[2][self::m:mtable][count(*)=2 and count(*[1])=1] and contains(@intent, 'binomial(')" + replace: + - x: "*[1]" + - x: "*[2]/*[1]/*[1]/*[1]" + - t: "⠰⠻" + - x: "*[2]/*[2]/*[1]/*[1]" + - x: "*[3]" + +# +# Matrix/Determinant rules +# matrix and determinant are the same other than "matrix"/"determinant" based on the bracketing chars +# we don't do spatial layout, instead the beginning/ending of each row uses the enlarged bracketing chars +- + name: default-matrix + tag: mrow + variables: + - RowStart: "*[1]" + - RowEnd: "*[3]" + match: + - "*[2][self::m:mtable] and" + - (IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|')) + replace: [x: "*[2]"] +- + name: default-mtable + tag: mtable + match: "." + replace: [x: "*"] +- + name: default + tag: [mtr, mlabeledtr] + match: "." + replace: + - test: + if: "preceding-sibling::*" + then: [t: "W⠠⠜W"] + - t: "" + - x: $RowStart + + # - test: + # if: .[self::m:mlabeledtr] + # then: + # - t: "⠗⠕⠺W⠇⠁⠃⠑⠇⠸⠒" # "row label:" + # - x: "*[1]/*" # contents of row label + - test: + if: .[self::m:mlabeledtr] + then: [x: "*[position()>1]"] + else: {x: "*"} + - x: $RowEnd + + +- + name: empty + tag: mtd + match: ".=''" + replace: + - t: "⠐⠂" + +- + name: default + tag: mtd + match: "." + replace: + - test: + if: "*" + then: + - test: + if: "preceding-sibling::*" + then: [t: "W"] + - x: "*" + # else nothing to braille + +- + name: no-content + tag: math + match: "not(*)" # empty + replace: [t: "W"] # not sure that is right, but this shouldn't happen + +- + name: default + tag: math + match: "." + variables: + - RowStart: "''" # empty string -- it needs to be set + - RowEnd: "''" # empty string -- it needs to be set + - NewScriptContext: "''" # empty string -- it needs to be set + - MatchingWhitespace: "false()" + replace: [x: "*"] + +- + name: empty-mrow + tag: mrow + match: "not(*)" + replace: [t: "W"] # not sure what is correct -- if in a fraction, probably something is better than nothing + +- + # From 14.5, 14.6, and 14.7 -- need to add block indicators around the argument unless it has parens + name: trig-functions + tag: mrow + match: "count(*)=3 and *[2]='\u2061' and + IsInDefinition(BaseNode(*[1]), 'Braille', 'TrigFunctionNames') and not(IsBracketed(*[3], '(', ')'))" + replace: + - x: "*[1]" + - t: "⠰" + - x: "*[3]" + - t: "⠆" + + +- + name: default + tag: mrow + match: "." + replace: [x: "*"] + + +- + # operator omission + name: operator-omission + tag: mo + # normally(?) this is an omission, but the case 6'2" causes problems due to WIRIS + # in that case, it doesn't put the ft/in in a superscript and thinks the space is an operator + # canonicalization moves them into with base , so this shouldn't be an omission for that case + match: ".='\u00A0' and @width > 1.1 and + not( preceding-sibling::*[1][self::m:msup and *[2][self::m:mo]] and + following-sibling::*[1][self::m:msup and *[2][self::m:mo]] )" + replace: + - t: "⠬" # empty space for omission + +- + # "∼" can be a prefix *operator* or an infix comparison -- they have different spacing. We catch the prefix version here + # Note: ASCII "~" has been canonicalized to occur only in a diacritical position + name: prefix-tilde + tag: mo + match: "translate(., '∼', '')='' and not(preceding-sibling::*) and parent::m:mrow" + replace: + - x: "text()" + +- + name: default + tag: mo + # add spaces around comparison operators unless they are in a script position, + variables: # ratio not spaced in UEB; also make sure it is an infix operator (e.g., "~" can be prefix) + - AddSpaces: "$UEB_UseSpacesAroundAllOperators" + match: "." + replace: + - test: + if: "$AddSpaces" + then: + - test: + if: "preceding-sibling::*" + then: [t: "W"] + - x: "text()" + - test: + if: "$AddSpaces and following-sibling::*" + then: [t: "W"] + + +- + name: default + tag: mn + match: "." + replace: + - x: "BrailleChars(., 'French')" +- + name: default + tag: [mi, mtext] + match: "." + replace: + - x: "BrailleChars(., 'French')" + +- + name: default + tag: mstyle + match: "." + replace: + - test: + if: "*" + then: [x: "*"] + # else do nothing -- no content + +- + name: mhchem-hack + # this captures the output for the mhchem's "<=>", "<<=>", and "<=>>" output (there are no Unicode arrows for them) + # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/NSoiffer/MathCAT/issues/60) + tag: mover + match: + - "*[1][substring(., 1, 1)='↽'] and" + - "*[2][substring(., string-length(), 1)='⇀']" + replace: + - t: "W" + - test: + if: "*[1][self::m:mrow]" + then_test: + if: "*[2][self::m:mrow]" + then: [x: "'🣑'"] # 0x1f8d1 -- this is currently unassigned and may get used by UTC at some point (<=>) + else: [x: "'🣓'"] # 0x1f8d3 -- this is currently unassigned and may get used by UTC at some point (<<=>) + else: [x: "'🣒'"] # 0x1f8d2 -- this is currently unassigned and may get used by UTC at some point (<=>>) + - t: "W" + +- + # special hack to catch double over/under bar built from nested mover/munder + name: doubled-line-hack-exception + tag: [mover, munder] + match: + - "*[2][self::m:mo and string-length(.) = 1 and (.='¯')] and" # has line/bar + - "( (self::m:mover and *[1][self::m:mover]) or (self::m:munder and *[1][self::m:munder]) ) and" # both mover/munder + - "*[1]/*[2][self::m:mo and string-length(.) = 1 and (.='¯')]" # other mover/munder has line/bar + replace: + - test: + if: "self::m:munder" + then: [t: "⠨"] + - t: "⠘⠸⠒" + - test: + if: "NeedsToBeGrouped(*[1], 'French', true())" + then: + - t: "⠰" + - x: "*[1]" + - t: "⠆" + else: [x: "*[1]"] + +- + name: single-char-exceptions # GTM 12 + tag: [mover, munder] + match: "*[2][string-length(.) = 1 and translate(., '_¯→.·˙~^⌄⌒','')='']" + replace: + - test: + if: "self::m:munder" + then: [t: "⠨"] + - test: + - if: "*[2][text()='_' or text()='¯']" + then: [t: "⠸⠒"] + - else_if: "*[2][text()='→']" + then: [t: "⠨⠒"] + - else_if: "*[2][text()='.' or text()='·' or text()='˙']" + then: [t: "⠂"] # FIX: couldn't find an example + - else_if: "*[2][text()='~']" + then: [t: "⠐⠸⠒"] + - else_if: "*[2][text()='^']" + then: [t: "⠘⠒"] + - else_if: "*[2][text()='⌄']" + then: [t: "⠸⠘⠒"] + else: [t: "⠈⠒"] # arc/⌒ + # The examples in 12.1 never group the base, but this seems dubious. Needs to be confirmed + # - test: + # if: "NeedsToBeGrouped(*[1], 'French', true())" + # then: + # - t: "⠰" + # - x: "*[1]" + # - t: "⠆" + # else: [x: "*[1]"] + - x: "*[1]" + +- + # In French braille, we drop the script indicators in both subscripts and superscripts for: + # msub: chemistry (e.g., H20) + # msup: primes + # We combine them here + name: single-char-exceptions + tag: [msub, msup] + match: "(self::m:msub and @data-chem-formula) or (self::m:msup and *[2][translate(., \"'′″‴\",'')=''])" + replace: + - test: + if: "NeedsToBeGrouped(*[1], 'French', true())" + then: + - t: "⠰" + - x: "*[1]" + - t: "⠆" + else: [x: "*[1]"] + - x: "*[2]" + +- + name: default + tag: [msub, msup, munder, mover] + match: "." + variables: [AddSpaces: "(self::m:munder or self::m:mover) and IsInDefinition(BaseNode(*[1]), 'Braille', 'NemethComparisonOperators')"] + replace: + - test: + if: "$AddSpaces" + then: [t: "W"] + - test: + if: "NeedsToBeGrouped(*[1], 'French', self::m:msub or self::m:msup)" + then: + - t: "⠰" + - x: "*[1]" + - t: "⠆" + else: [x: "*[1]"] + - with: # this could be tighter and be just around the *[2] part, but this is consistent with msubsup below + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - test: + - if: "self::m:msup" + then: [t: "⠈"] + - else_if: "self::m:msub" + then: [t: "⠢"] + - else_if: "self::m:munder" + then: [t: "⠢⠢"] + else: [t: "⠈⠈"] # mover + - test: + if: "NeedsToBeGrouped(*[2], 'French', false())" + then: + - t: "⠰" + - x: "*[2]" + - t: "⠆" + else: [x: "*[2]"] + - t: "#" # signal end script/numeric mode + - test: + if: "$AddSpaces" + then: [t: "W"] + + + +- + name: msubsup_default_mmultiscripts_equiv + tag: [msubsup, munderover, mmultiscripts] # mmultiscripts with only sub/sup postscript + match: "count(*)=3" # catches mmultiscripts equiv + variables: [IsPrimeSuperScript: "not(self::m:munderover) and *[3][translate(., \"'′″‴\",'')='']"] + replace: + - test: + if: "NeedsToBeGrouped(*[1], 'French', self::m:msubsup)" + then: + - t: "⠰" + - x: "*[1]" + - t: "⠆" + else: [x: "*[1]"] + - test: + if: "$IsPrimeSuperScript" + then: [x: "*[3]"] + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - test: + if: "self::m:munderover" + then: [t: "⠢⠢"] + else: [t: "⠢"] + - test: + if: "NeedsToBeGrouped(*[2], 'French', false())" + then: + - t: "⠰" + - x: "*[2]" + - t: "⠆" + else: [x: "*[2]"] + - test: + if: "not($IsPrimeSuperScript)" + then: + - test: + if: "self::m:munderover" + then: [t: "⠈⠈"] + else: [t: "⠈"] + - test: + if: "NeedsToBeGrouped(*[3], 'French', false())" + then: + - t: "⠰" + - x: "*[3]" + - t: "⠆" + else: [x: "*[3]"] + + +- +# See 9.5, remark before last example about atomic numbers (they are *after* the base preceded by dot 6) + name: atomic-number + tag: mmultiscripts + match: "count(*)=4 and *[2][self::m:mprescripts] and @data-chem-formula" + replace: + - x: "*[1]" + - test: + if: "not(*[3][self::m:none])" + then: + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - t: "⠠⠢" + - test: + if: "NeedsToBeGrouped(*[3], 'French', true())" + then: + - t: "⠰" + - x: "*[3]" + - t: "⠆" + else: [x: "*[3]"] + - test: + if: "not(*[4][self::m:none])" + then: + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - t: "⠠⠈" + - test: + if: "NeedsToBeGrouped(*[4], 'French', false())" + then: + - t: "⠰" + - x: "*[4]" + - t: "⠆" + else: [x: "*[4]"] + +- + name: default + 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: + - test: + if: "$Prescripts" + then: + - test: + if: "not($Prescripts[1][self::m:none])" + then: + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - t: "⠢" + - test: + if: "NeedsToBeGrouped($Prescripts[1], 'French', true())" + then: + - t: "⠰" + - x: "$Prescripts[1]" + - t: "⠆" + else: [x: "$Prescripts[1]"] + - test: + if: "not($Prescripts[2][self::m:none])" + then: + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - t: "⠈" + - test: + if: "NeedsToBeGrouped($Prescripts[2], 'French', false())" + then: + - t: "⠰" + - x: "$Prescripts[2]" + - t: "⠆" + else: [x: "$Prescripts[2]"] + - test: + if: "count($Prescripts)>2" + then: + - test: + if: "not($Prescripts[3][self::m:none])" + then: + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - t: "⠢" + - test: + if: "NeedsToBeGrouped($Prescripts[3], 'French', false())" + then: + - t: "⠰" + - x: "$Prescripts[3]" + - t: "⠆" + else: [x: "$Prescripts[3]"] + - test: + if: "not($Prescripts[4][self::m:none])" + then: + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - t: "⠈" + - test: + if: "NeedsToBeGrouped($Prescripts[4], 'French', false())" + then: + - t: "⠰" + - x: "$Prescripts[4]" + - t: "⠆" + else: [x: "$Prescripts[4]"] + - test: + if: "count($Prescripts) > 4" # give up and just dump them out so at least the content is there + then: [x: "$Prescripts[position() > 4]"] + + - x: "*[1]" + - test: + if: "$Postscripts" + then: + - test: + if: "not($Postscripts[1][self::m:none])" + then: + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - t: "⠢" + - test: + if: "NeedsToBeGrouped($Postscripts[1], 'French', false())" + then: + - t: "⠰" + - x: "$Postscripts[1]" + - t: "⠆" + else: [x: "$Postscripts[1]"] + - test: + if: "not($Postscripts[2][self::m:none])" + then: + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - t: "⠈" + - test: + if: "NeedsToBeGrouped($Postscripts[2], 'French', false())" + then: + - t: "⠰" + - x: "$Postscripts[2]" + - t: "⠆" + else: [x: "$Postscripts[2]"] + - test: + if: "count($Postscripts)>2" + then: + - test: + if: "not($Postscripts[3][self::m:none])" + then: + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - t: "⠢" + - test: + if: "NeedsToBeGrouped($Postscripts[3], 'French', false())" + then: + - t: "⠰" + - x: "$Postscripts[3]" + - t: "⠆" + else: [x: "$Postscripts[3]"] + - test: + if: "not($Postscripts[4][self::m:none])" + then: + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - t: "⠈" + - test: + if: "NeedsToBeGrouped($Postscripts[4], 'French', false())" + then: + - t: "⠰" + - x: "$Postscripts[4]" + - t: "⠆" + else: [x: "$Postscripts[4]"] + - test: + if: "count($Postscripts)>4" + then: + - test: + if: "not($Postscripts[5][self::m:none])" + then: + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - t: "⠢" + - test: + if: "NeedsToBeGrouped($Postscripts[5], 'French', false())" + then: + - t: "⠰" + - x: "$Postscripts[5]" + - t: "⠆" + else: [x: "$Postscripts[5]"] + - test: + if: "not($Postscripts[6][self::m:none])" + then: + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - t: "⠈" + - test: + if: "NeedsToBeGrouped($Postscripts[6], 'French', false())" + then: + - t: "⠰" + - x: "$Postscripts[6]" + - t: "⠆" + else: [x: "$Postscripts[6]"] + - test: + if: "count($Postscripts)>6" + then: + - test: + if: "not($Postscripts[7][self::m:none])" + then: + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - t: "⠢" + - test: + if: "NeedsToBeGrouped($Postscripts[7], 'French', false())" + then: + - t: "⠰" + - x: "$Postscripts[7]" + - t: "⠆" + else: [x: "$Postscripts[7]"] + - test: + if: "not($Postscripts[8][self::m:none])" + then: + - with: + variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string + replace: + - t: "⠈" + - test: + if: "NeedsToBeGrouped($Postscripts[8], 'French', false())" + then: + - t: "⠰" + - x: "$Postscripts[8]" + - t: "⠆" + else: [x: "$Postscripts[8]"] + - test: + if: "count($Postscripts) > 8" # give up and just dump them out so at least the content is there + then: + - x: "$Postscripts[position() > 8]" + - t: "#" # signal end script/numeric mode + + +- + # Note: @notation can contain more than one value + # I don't think UEB has a good way to represent all notations, especially when in combination + # Note: + # Shape indicator: ⠫ + # Physical enclosure: ⠪ (think plus inside of a circle) + # Superposition indicator: ⠯ (think contour integral) + # Cancellation: ⠈⠱ (line through previous char -- will use for horizontal, vertical, diagonal lines) + # Line over/under: use GTM 12 bar rule (over ⠱, under ⠠⠱) + # Horizontal juxtaposition indicator: ⠿ + # Vertical juxtaposition indicator: ⠻ (think ≗) + # Termination indicator: ⠱ + # + # We place left and right outside of other notations + # Boxes and circle are does as physical enclosure (shape before base) + # Arrows are done as superposition (after base) + # top and bottom are done as "bars" (GTM 12) (after base) + + name: default + tag: menclose + match: "." + # FIX: can't find a rule that says anything about comparison operator spacing and enclosure + variables: [AddSpaces: "parent::*[self::m:mrow] and *[1][ self::m:mo and IsInDefinition(., 'Braille', 'NemethComparisonOperators')]"] + replace: + - test: + if: "contains(concat(' ', normalize-space(@notation), ' '), ' left ')" #avoid 'leftarrow' + then: [t: "⠸"] + - test: + if: "contains(@notation,'box')" # box and roundedbox + then: + # - test: + # if: "$AddSpaces" + # then: [t: " "] + - t: "1⠫⠼⠙" # square (no rectangle in UEB) + - test: + if: "contains(@notation,'circle')" + then: + # - test: + # if: "$AddSpaces" + # then: [t: " "] + - t: "1⠫⠿" # circle (no oval in UEB) +# ??? What should happen with arrow? +# If there is a box/circle with arrows only and an empty child, +# then it acts like the arrow is the child +# If there are only arrows for 'notation', then maybe rule 112 applies (superposition), +# but the examples aren't similar. In that case, the arrow acts like 'box' and the child is the content... maybe +# +# - test: +# if: "contains(@notation,'leftarrow')" +# then: [t: left arrow, pause: short] +# - test: +# if: "contains(concat(' ', normalize-space(@notation), ' '), ' rightarrow ')" +# then: [t: right arrow, pause: short] +# - test: +# if: "contains(@notation,'northeastarrow')" +# then: [t: northeast arrow, pause: short] +# - test: +# if: "contains(concat(' ', normalize-space(@notation), ' '), ' southeastarrow ')" +# then: [t: southeast arrow, pause: short] +# - test: +# if: "contains(concat(' ', normalize-space(@notation), ' '), ' southwestarrow ')" +# then: [t: southwest arrow, pause: short] +# - test: +# if: "contains(@notation,'northwestarrow')" +# then: [t: northwest arrow, pause: short] +# - test: +# if: "contains(@notation,'updownarrow')" +# then: [t: double ended vertical arrow, pause: short] +# - test: +# if: "contains(@notation,'leftrightarrow')" +# then: [t: double ended horizontal arrow, pause: short] +# - test: +# if: "contains(@notation,'northeastsouthwestarrow')" +# then: [t: double ended up diagonal arrow, pause: short] +# - test: +# if: "contains(@notation,'northwestsoutheastarrow')" +# then: [t: double ended down diagonal arrow, pause: short] +# - test: +# if: ".[contains(@notation,'actuarial')]" +# then: [t: actuarial symbol, pause: short] +# - test: +# if: ".[contains(@notation,'madrub')]" +# then: [t: arabic factorial symbol, pause: short] +# - test: +# if: ".[contains(@notation,'longdiv') or not(@notation) or normalize-space(@notation) ='']" # default +# then: [t: long division symbol, pause: short] +# - test: +# if: ".[contains(@notation,'radical')]" +# then: [t: square root, pause: short] + + - test: + if: "NeedsToBeGrouped(*[1], 'French', false())" + then: + - t: "⠰" + - x: "*[1]" + - t: "⠆" + else: [x: "*[1]"] + + # - test: + # if: "contains(@notation,'phasorangle')" #FIX: what should this be??? + # then: [t: "⠫⠪⠸⠫"] + - test: + if: "contains(@notation,'arrow')" # all the arrows + then: + - test: + - if: "contains(@notation,'rightarrow')" + then: [t: "1⠳⠕"] + - else_if: "contains(@notation,'leftarrow')" + then: [t: "1⠳⠪"] + - else_if: "contains(@notation,'uparrow')" + then: [t: "1⠳⠬ "] + - else_if: "contains(@notation,'downarrow')" + then: [t: "1⠳⠩"] + - else_if: "contains(@notation,'northeastarrow')" + then: [t: "1⠳⠎"] + - else_if: "contains(@notation,'southeastarrow')" + then: [t: "1⠳⠣"] + - else_if: "contains(@notation,'northwestarrow')" + then: [t: "1⠳⠱"] + - else_if: "contains(@notation,'southwestarrow')" + then: [t: "1⠳⠜"] + - else_if: "contains(@notation,'leftrightarrow')" + then: [t: "1⠳⠺⠗⠕"] + - else_if: "contains(@notation,'updownarrow')" + then: [t: "1⠳⠺⠗⠬"] + - else_if: "contains(@notation,'northeastsouthwestarrow')" + then: [t: "1⠳⠺⠗⠎"] + - else_if: "contains(@notation,'northwestsoutheastarrow')" + then: [t: "1⠳⠺⠗⠣"] + - test: + if: "contains(@notation,'top')" + then: [t: "⠱"] + - test: + if: "contains(@notation,'bottom')" + then: [t: "⠠⠱"] + - test: + if: "contains(@notation,'updiagonalstrike') or contains(@notation,'downdiagonalstrike') or + contains(@notation,'verticalstrike') or contains(@notation,'horizontalstrike')" + then: + - t: "1⠈⠱" # cancellation + - test: + if: + - "not($AddSpaces) and contains(@notation,'box') or contains(@notation,'circle') or" + - "contains(@notation,'arrow') or contains(@notation,'phasorangle')" + then: + - t: "⠻" # terminate shape + # - test: + # if: "$AddSpaces" + # then: [t: " "] + - test: + if: "contains(concat(' ', normalize-space(@notation), ' '), ' right ')" #avoid 'rightarrow' + then: [t: "⠸"] + + + +- + name: default + tag: ms + match: "." + replace: + - test: + if: "string(@lquote)!=''" + then: [x: "@lquote"] + else: [t: "⠄⠄"] + - x: "BrailleChars(., 'French')" + - test: + if: "string(@rquote)!=''" + then: [x: "@rquote"] + else: [t: "⠄⠄"] + +- + name: default + tag: semantics + match: "." + replace: + - x: "*[1]" #/ FIX: should prioritize @encoding="MathML-Presentation" and @encoding="application/mathml-presentation+xml" + +- + name: default-children + tag: "*" + match: "*" # make sure there are children + replace: + - t: "unknown math m l element" + - x: "name(.)" + - x: "*" + +- + # at this point, we know there are no children -- might be no text + name: default-no-children + tag: "*" + match: "text()" + replace: + - t: unknown math m l element + - x: "name(.)" + - x: "text()" + +- + name: default-no-text + tag: "*" + match: "." + replace: + - t: "empty unknown math m l element" + - x: "name(.)" diff --git a/Rules/Braille/French/definitions.yaml b/Rules/Braille/French/definitions.yaml new file mode 100644 index 000000000..4d0c77efb --- /dev/null +++ b/Rules/Braille/French/definitions.yaml @@ -0,0 +1,2 @@ +--- +- include: "../definitions.yaml" diff --git a/Rules/Braille/French/unicode-full.yaml b/Rules/Braille/French/unicode-full.yaml new file mode 100644 index 000000000..91054d0cb --- /dev/null +++ b/Rules/Braille/French/unicode-full.yaml @@ -0,0 +1,1027 @@ +--- + + - "⇌": [t: "⠸⠻"] # 0x21CC (Rightwards harpoon over leftwards harpoon (equilibrium)) +# - "⥂": [t: ""] # 0x2942 (Rightwards arrow above short leftwards arrow (equilibrium, trend to the right)) +# - "⥄": [t: ""] # 0x2944 (Short rightwards arrow above leftwards arrow (equilibrium, trend to the left)) + +# Mappings for some new Unicode chemistry arrows/symbols +# These repeat the short arrow version because that is what is in the spec (GTM 16), but the spec should really use the long version + - "🣑": [t: "⠸⠻"] # 0x1F8D1 LONG RIGHTWARDS HARPOON OVER LONG LEFTWARDS HARPOON (equilibrium) +# - "🣒": [t: ""] # 0x1f8d2 LONG RIGHTWARDS HARPOON ABOVE SHORT LEFTWARDS HARPOON(equilibrium, trend to the right) +# - "🣓": [t: ""] # 0x1f8d3 SHORT RIGHTWARDS HARPOON ABOVE LONG LEFTWARDS HARPOON (equilibrium, trend to the left)) + +# - "🣐": [t: ""] # 0x1F8D0 LONG RIGHTWARDS ARROW OVER LONG LEFTWARDS ARROW +# - "🣕": [t: ""] # 0x1F8D5 SHORT LEFTWARDS HARPOON ABOVELONG RIGHTWARDS HARPOON +# - "🣖": [t: ""] # 0x1F8D6 LONG RIGHTWARDS ARROW WITH THROUGH X +# - "🣗": [t: ""] # 0x1F8D7 LONG RIGHTWARDS ARROW WITH DOUBLE SLASH +# - "": [t: ""] # 0x1F8D8 LONG LEFT RIGHT ARROW WITH DEPENDENT LOBE (no clue what to do) + + - "⅐": [t: "⠡⠌⠻"] # 0x2150 (vulgar fraction one seventh) + - "⅑": [t: "⠡⠌⠪"] # 0x2151 (vulgar fraction one ninth) + - "⅓": [t: "⠡⠌⠩"] # 0x2153 (vulgar fraction one third) + - "⅔": [t: "⠣⠌⠩"] # 0x2154 (vulgar fraction two thirds) + - "⅕": [t: "⠡⠌⠱"] # 0x2155 (vulgar fraction one fifth) + - "⅖": [t: "⠣⠌⠱"] # 0x2156 (vulgar fraction two fifths) + - "⅗": [t: "⠩⠌⠱"] # 0x2157 (vulgar fraction three fifths) + - "⅘": [t: "⠹⠌⠱"] # 0x2158 (vulgar fraction four fifths) + - "⅙": [t: "⠡⠌⠫"] # 0x2159 (vulgar fraction one sixth) + - "⅚": [t: "⠱⠌⠫"] # 0x215A (vulgar fraction five sixths) + - "⅛": [t: "⠡⠌⠳"] # 0x215B (vulgar fraction one eighth) + - "⅜": [t: "⠩⠌⠳"] # 0x215C (vulgar fraction three eighths) + - "⅝": [t: "⠱⠌⠳"] # 0x215D (vulgar fraction five eighths) + - "⅞": [t: "⠻⠌⠳"] # 0x215E (vulgar fraction seven eighths) + - "↉": [t: "⠼⠌⠩"] # 0x2189 (vulgar fraction zero thirds) + + + + - "𝐚": [t: "BL⠁"] # 0x1d41a + - "𝐛": [t: "BL⠃"] # 0x1d41b + - "𝐜": [t: "BL⠉"] # 0x1d41c + - "𝐝": [t: "BL⠙"] # 0x1d41d + - "𝐞": [t: "BL⠑"] # 0x1d41e + - "𝐟": [t: "BL⠋"] # 0x1d41f + - "𝐠": [t: "BL⠛"] # 0x1d420 + - "𝐡": [t: "BL⠓"] # 0x1d421 + - "𝐢": [t: "BL⠊"] # 0x1d422 + - "𝐣": [t: "BL⠚"] # 0x1d423 + - "𝐤": [t: "BL⠅"] # 0x1d424 + - "𝐥": [t: "BL⠇"] # 0x1d425 + - "𝐦": [t: "BL⠍"] # 0x1d426 + - "𝐧": [t: "BL⠝"] # 0x1d427 + - "𝐨": [t: "BL⠕"] # 0x1d428 + - "𝐩": [t: "BL⠏"] # 0x1d429 + - "𝐪": [t: "BL⠟"] # 0x1d42a + - "𝐫": [t: "BL⠗"] # 0x1d42b + - "𝐬": [t: "BL⠎"] # 0x1d42c + - "𝐭": [t: "BL⠞"] # 0x1d42d + - "𝐮": [t: "BL⠥"] # 0x1d42e + - "𝐯": [t: "BL⠧"] # 0x1d42f + - "𝐰": [t: "BL⠺"] # 0x1d430 + - "𝐱": [t: "BL⠭"] # 0x1d431 + - "𝐲": [t: "BL⠽"] # 0x1d432 + - "𝐳": [t: "BL⠵"] # 0x1d433 + + - "𝐀": [t: "BCL⠁"] # 0x1d400 + - "𝐁": [t: "BCL⠃"] # 0x1d401 + - "𝐂": [t: "BCL⠉"] # 0x1d402 + - "𝐃": [t: "BCL⠙"] # 0x1d403 + - "𝐄": [t: "BCL⠑"] # 0x1d404 + - "𝐅": [t: "BCL⠋"] # 0x1d405 + - "𝐆": [t: "BCL⠛"] # 0x1d406 + - "𝐇": [t: "BCL⠓"] # 0x1d407 + - "𝐈": [t: "BCL⠊"] # 0x1d408 + - "𝐉": [t: "BCL⠚"] # 0x1d409 + - "𝐊": [t: "BCL⠅"] # 0x1d40a + - "𝐋": [t: "BCL⠇"] # 0x1d40b + - "𝐌": [t: "BCL⠍"] # 0x1d40c + - "𝐍": [t: "BCL⠝"] # 0x1d40d + - "𝐎": [t: "BCL⠕"] # 0x1d40e + - "𝐏": [t: "BCL⠏"] # 0x1d40f + - "𝐐": [t: "BCL⠟"] # 0x1d410 + - "𝐑": [t: "BCL⠗"] # 0x1d411 + - "𝐒": [t: "BCL⠎"] # 0x1d412 + - "𝐓": [t: "BCL⠞"] # 0x1d413 + - "𝐔": [t: "BCL⠥"] # 0x1d414 + - "𝐕": [t: "BCL⠧"] # 0x1d415 + - "𝐖": [t: "BCL⠺"] # 0x1d416 + - "𝐗": [t: "BCL⠭"] # 0x1d417 + - "𝐘": [t: "BCL⠽"] # 0x1d418 + - "𝐙": [t: "BCL⠵"] # 0x1d419 + +# don't mark these as italic + - "𝑎": [t: "L⠁"] # 0x1d44e + - "𝑏": [t: "L⠃"] # 0x1d44f + - "𝑐": [t: "L⠉"] # 0x1d450 + - "𝑑": [t: "L⠙"] # 0x1d451 + - "𝑒": [t: "L⠑"] # 0x1d452 + - "𝑓": [t: "L⠋"] # 0x1d453 + - "𝑔": [t: "L⠛"] # 0x1d454 + - "𝑕": [t: "L⠓"] # 0x1d455 + - "𝑖": [t: "L⠊"] # 0x1d456 + - "𝑗": [t: "L⠚"] # 0x1d457 + - "𝑘": [t: "L⠅"] # 0x1d458 + - "𝑙": [t: "L⠇"] # 0x1d459 + - "𝑚": [t: "L⠍"] # 0x1d45a + - "𝑛": [t: "L⠝"] # 0x1d45b + - "𝑜": [t: "L⠕"] # 0x1d45c + - "𝑝": [t: "L⠏"] # 0x1d45d + - "𝑞": [t: "L⠟"] # 0x1d45e + - "𝑟": [t: "L⠗"] # 0x1d45f + - "𝑠": [t: "L⠎"] # 0x1d460 + - "𝑡": [t: "L⠞"] # 0x1d461 + - "𝑢": [t: "L⠥"] # 0x1d462 + - "𝑣": [t: "L⠧"] # 0x1d463 + - "𝑤": [t: "L⠺"] # 0x1d464 + - "𝑥": [t: "L⠭"] # 0x1d465 + - "𝑦": [t: "L⠽"] # 0x1d466 + - "𝑧": [t: "L⠵"] # 0x1d467 + + - "𝐴": [t: "CL⠁"] # 0x1d434 + - "𝐵": [t: "CL⠃"] # 0x1d435 + - "𝐶": [t: "CL⠉"] # 0x1d436 + - "𝐷": [t: "CL⠙"] # 0x1d437 + - "𝐸": [t: "CL⠑"] # 0x1d438 + - "𝐹": [t: "CL⠋"] # 0x1d439 + - "𝐺": [t: "CL⠛"] # 0x1d43a + - "𝐻": [t: "CL⠓"] # 0x1d43b + - "𝐼": [t: "CL⠊"] # 0x1d43c + - "𝐽": [t: "CL⠚"] # 0x1d43d + - "𝐾": [t: "CL⠅"] # 0x1d43e + - "𝐿": [t: "CL⠇"] # 0x1d43f + - "𝑀": [t: "CL⠍"] # 0x1d440 + - "𝑁": [t: "CL⠝"] # 0x1d441 + - "𝑂": [t: "CL⠕"] # 0x1d442 + - "𝑃": [t: "CL⠏"] # 0x1d443 + - "𝑄": [t: "CL⠟"] # 0x1d444 + - "𝑅": [t: "CL⠗"] # 0x1d445 + - "𝑆": [t: "CL⠎"] # 0x1d446 + - "𝑇": [t: "CL⠞"] # 0x1d447 + - "𝑈": [t: "CL⠥"] # 0x1d448 + - "𝑉": [t: "CL⠧"] # 0x1d449 + - "𝑊": [t: "CL⠺"] # 0x1d44a + - "𝑋": [t: "CL⠭"] # 0x1d44b + - "𝑌": [t: "CL⠽"] # 0x1d44c + - "𝑍": [t: "CL⠵"] # 0x1d44d + + - "𝒂": [t: "BIL⠁"] # 0x1d482 + - "𝒃": [t: "BIL⠃"] # 0x1d483 + - "𝒄": [t: "BIL⠉"] # 0x1d484 + - "𝒅": [t: "BIL⠙"] # 0x1d485 + - "𝒆": [t: "BIL⠑"] # 0x1d486 + - "𝒇": [t: "BIL⠋"] # 0x1d487 + - "𝒈": [t: "BIL⠛"] # 0x1d488 + - "𝒉": [t: "BIL⠓"] # 0x1d489 + - "𝒊": [t: "BIL⠊"] # 0x1d48a + - "𝒋": [t: "BIL⠚"] # 0x1d48b + - "𝒌": [t: "BIL⠅"] # 0x1d48c + - "𝒍": [t: "BIL⠇"] # 0x1d48d + - "𝒎": [t: "BIL⠍"] # 0x1d48e + - "𝒏": [t: "BIL⠝"] # 0x1d48f + - "𝒐": [t: "BIL⠕"] # 0x1d490 + - "𝒑": [t: "BIL⠏"] # 0x1d491 + - "𝒒": [t: "BIL⠟"] # 0x1d492 + - "𝒓": [t: "BIL⠗"] # 0x1d493 + - "𝒔": [t: "BIL⠎"] # 0x1d494 + - "𝒕": [t: "BIL⠞"] # 0x1d495 + - "𝒖": [t: "BIL⠥"] # 0x1d496 + - "𝒗": [t: "BIL⠧"] # 0x1d497 + - "𝒘": [t: "BIL⠺"] # 0x1d498 + - "𝒙": [t: "BIL⠭"] # 0x1d499 + - "𝒚": [t: "BIL⠽"] # 0x1d49a + - "𝒛": [t: "BIL⠵"] # 0x1d49b + + - "𝑨": [t: "BICL⠁"] # 0x1d468 + - "𝑩": [t: "BICL⠃"] # 0x1d469 + - "𝑪": [t: "BICL⠉"] # 0x1d46a + - "𝑫": [t: "BICL⠙"] # 0x1d46b + - "𝑬": [t: "BICL⠑"] # 0x1d46c + - "𝑭": [t: "BICL⠋"] # 0x1d46d + - "𝑮": [t: "BICL⠛"] # 0x1d46e + - "𝑯": [t: "BICL⠓"] # 0x1d46f + - "𝑰": [t: "BICL⠊"] # 0x1d470 + - "𝑱": [t: "BICL⠚"] # 0x1d471 + - "𝑲": [t: "BICL⠅"] # 0x1d472 + - "𝑳": [t: "BICL⠇"] # 0x1d473 + - "𝑴": [t: "BICL⠍"] # 0x1d474 + - "𝑵": [t: "BICL⠝"] # 0x1d475 + - "𝑶": [t: "BICL⠕"] # 0x1d476 + - "𝑷": [t: "BICL⠏"] # 0x1d477 + - "𝑸": [t: "BICL⠟"] # 0x1d478 + - "𝑹": [t: "BICL⠗"] # 0x1d479 + - "𝑺": [t: "BICL⠎"] # 0x1d47a + - "𝑻": [t: "BICL⠞"] # 0x1d47b + - "𝑼": [t: "BICL⠥"] # 0x1d47c + - "𝑽": [t: "BICL⠧"] # 0x1d47d + - "𝑾": [t: "BICL⠺"] # 0x1d47e + - "𝑿": [t: "BICL⠭"] # 0x1d47f + - "𝒀": [t: "BICL⠽"] # 0x1d480 + - "𝒁": [t: "BICL⠵"] # 0x1d481 + + - "𝒶": [t: "TsL⠁"] # 0x1d4b6 + - "𝒷": [t: "TsL⠃"] # 0x1d4b7 + - "𝒸": [t: "TsL⠉"] # 0x1d4b8 + - "𝒹": [t: "TsL⠙"] # 0x1d4b9 + - "𝒺": [t: "TsL⠑"] # 0x1d4ba + - "𝒻": [t: "TsL⠋"] # 0x1d4bb + - "𝒼": [t: "TsL⠛"] # 0x1d4bc + - "𝒽": [t: "TsL⠓"] # 0x1d4bd + - "𝒾": [t: "TsL⠊"] # 0x1d4be + - "𝒿": [t: "TsL⠚"] # 0x1d4bf + - "𝓀": [t: "TsL⠅"] # 0x1d4c0 + - "𝓁": [t: "TsL⠇"] # 0x1d4c1 + - "𝓂": [t: "TsL⠍"] # 0x1d4c2 + - "𝓃": [t: "TsL⠝"] # 0x1d4c3 + - "𝓄": [t: "TsL⠕"] # 0x1d4c4 + - "𝓅": [t: "TsL⠏"] # 0x1d4c5 + - "𝓆": [t: "TsL⠟"] # 0x1d4c6 + - "𝓇": [t: "TsL⠗"] # 0x1d4c7 + - "𝓈": [t: "TsL⠎"] # 0x1d4c8 + - "𝓉": [t: "TsL⠞"] # 0x1d4c9 + - "𝓊": [t: "TsL⠥"] # 0x1d4ca + - "𝓋": [t: "TsL⠧"] # 0x1d4cb + - "𝓌": [t: "TsL⠺"] # 0x1d4cc + - "𝓍": [t: "TsL⠭"] # 0x1d4cd + - "𝓎": [t: "TsL⠽"] # 0x1d4ce + - "𝓏": [t: "TsL⠵"] # 0x1d4cf + + - "𝒜": [t: "TsCL⠁"] # 0x1d49c + - "𝒝": [t: "TsCL⠃"] # 0x1d49d + - "𝒞": [t: "TsCL⠉"] # 0x1d49e + - "𝒟": [t: "TsCL⠙"] # 0x1d49f + - "𝒠": [t: "TsCL⠑"] # 0x1d4a0 + - "𝒡": [t: "TsCL⠋"] # 0x1d4a1 + - "𝒢": [t: "TsCL⠛"] # 0x1d4a2 + - "𝒣": [t: "TsCL⠓"] # 0x1d4a3 + - "𝒤": [t: "TsCL⠊"] # 0x1d4a4 + - "𝒥": [t: "TsCL⠚"] # 0x1d4a5 + - "𝒦": [t: "TsCL⠅"] # 0x1d4a6 + - "𝒧": [t: "TsCL⠇"] # 0x1d4a7 + - "𝒨": [t: "TsCL⠍"] # 0x1d4a8 + - "𝒩": [t: "TsCL⠝"] # 0x1d4a9 + - "𝒪": [t: "TsCL⠕"] # 0x1d4aa + - "𝒫": [t: "TsCL⠏"] # 0x1d4ab + - "𝒬": [t: "TsCL⠟"] # 0x1d4ac + - "𝒭": [t: "TsCL⠗"] # 0x1d4ad + - "𝒮": [t: "TsCL⠎"] # 0x1d4ae + - "𝒯": [t: "TsCL⠞"] # 0x1d4af + - "𝒰": [t: "TsCL⠥"] # 0x1d4b0 + - "𝒱": [t: "TsCL⠧"] # 0x1d4b1 + - "𝒲": [t: "TsCL⠺"] # 0x1d4b2 + - "𝒳": [t: "TsCL⠭"] # 0x1d4b3 + - "𝒴": [t: "TsCL⠽"] # 0x1d4b4 + - "𝒵": [t: "TsCL⠵"] # 0x1d4b5 + + - "ℊ": [t: "TL⠛⠁"] # 0x210a (Script Small G) + - "ℋ": [t: "TCL⠓⠁"] # 0x210b (Script Capital H) + - "ℒ": [t: "TCL⠇"] # 0x2113 (Script Capital L) + - "ℓ": [t: "TL⠇"] # 0x2113 (Script Small L) + - "℘": [t: "TCL⠏"] # 0x2118 (Script Capital P) + - "ℛ": [t: "TCL⠗"] # 0x211B (Script Capital R) + - "ℯ": [t: "TL⠑"] # 0x212F (Script Small E) + - "ℰ": [t: "TCL⠑"] # 0x2130 (Script Capital E) + - "ℱ": [t: "TCL⠋"] # 0x2131 (Script Capital F) + - "ℳ": [t: "TCL⠍"] # 0x2133 (Script Capital M) + - "ℴ": [t: "TL⠕"] # 0x21334 (Script Small O) + - "𝓪": [t: "BTsL⠁"] # 0x1d4ea + - "𝓫": [t: "BTsL⠃"] # 0x1d4eb + - "𝓬": [t: "BTsL⠉"] # 0x1d4ec + - "𝓭": [t: "BTsL⠙"] # 0x1d4ed + - "𝓮": [t: "BTsL⠑"] # 0x1d4ee + - "𝓯": [t: "BTsL⠋"] # 0x1d4ef + - "𝓰": [t: "BTsL⠛"] # 0x1d4f0 + - "𝓱": [t: "BTsL⠓"] # 0x1d4f1 + - "𝓲": [t: "BTsL⠊"] # 0x1d4f2 + - "𝓳": [t: "BTsL⠚"] # 0x1d4f3 + - "𝓴": [t: "BTsL⠅"] # 0x1d4f4 + - "𝓵": [t: "BTsL⠇"] # 0x1d4f5 + - "𝓶": [t: "BTsL⠍"] # 0x1d4f6 + - "𝓷": [t: "BTsL⠝"] # 0x1d4f7 + - "𝓸": [t: "BTsL⠕"] # 0x1d4f8 + - "𝓹": [t: "BTsL⠏"] # 0x1d4f9 + - "𝓺": [t: "BTsL⠟"] # 0x1d4fa + - "𝓻": [t: "BTsL⠗"] # 0x1d4fb + - "𝓼": [t: "BTsL⠎"] # 0x1d4fc + - "𝓽": [t: "BTsL⠞"] # 0x1d4fd + - "𝓾": [t: "BTsL⠥"] # 0x1d4fe + - "𝓿": [t: "BTsL⠧"] # 0x1d4ff + - "𝔀": [t: "BTsL⠺"] # 0x1d500 + - "𝔁": [t: "BTsL⠭"] # 0x1d501 + - "𝔂": [t: "BTsL⠽"] # 0x1d502 + - "𝔃": [t: "BTsL⠵"] # 0x1d503 + + - "𝓐": [t: "BTsCL⠁"] # 0x1d4d0 + - "𝓑": [t: "BTsCL⠃"] # 0x1d4d1 + - "𝓒": [t: "BTsCL⠉"] # 0x1d4d2 + - "𝓓": [t: "BTsCL⠙"] # 0x1d4d3 + - "𝓔": [t: "BTsCL⠑"] # 0x1d4d4 + - "𝓕": [t: "BTsCL⠋"] # 0x1d4d5 + - "𝓖": [t: "BTsCL⠛"] # 0x1d4d6 + - "𝓗": [t: "BTsCL⠓"] # 0x1d4d7 + - "𝓘": [t: "BTsCL⠊"] # 0x1d4d8 + - "𝓙": [t: "BTsCL⠚"] # 0x1d4d9 + - "𝓚": [t: "BTsCL⠅"] # 0x1d4da + - "𝓛": [t: "BTsCL⠇"] # 0x1d4db + - "𝓜": [t: "BTsCL⠍"] # 0x1d4dc + - "𝓝": [t: "BTsCL⠝"] # 0x1d4dd + - "𝓞": [t: "BTsCL⠕"] # 0x1d4de + - "𝓟": [t: "BTsCL⠏"] # 0x1d4df + - "𝓠": [t: "BTsCL⠟"] # 0x1d4e0 + - "𝓡": [t: "BTsCL⠗"] # 0x1d4e1 + - "𝓢": [t: "BTsCL⠎"] # 0x1d4e2 + - "𝓣": [t: "BTsCL⠞"] # 0x1d4e3 + - "𝓤": [t: "BTsCL⠥"] # 0x1d4e4 + - "𝓥": [t: "BTsCL⠧"] # 0x1d4e5 + - "𝓦": [t: "BTsCL⠺"] # 0x1d4e6 + - "𝓧": [t: "BTsCL⠭"] # 0x1d4e7 + - "𝓨": [t: "BTsCL⠽"] # 0x1d4e8 + - "𝓩": [t: "BTsCL⠵"] # 0x1d4e9 + + - "𝔞": [t: "DL⠁"] # 0x1d51e + - "𝔟": [t: "DL⠃"] # 0x1d51f + - "𝔠": [t: "DL⠉"] # 0x1d520 + - "𝔡": [t: "DL⠙"] # 0x1d521 + - "𝔢": [t: "DL⠑"] # 0x1d522 + - "𝔣": [t: "DL⠋"] # 0x1d523 + - "𝔤": [t: "DL⠛"] # 0x1d524 + - "𝔥": [t: "DL⠓"] # 0x1d525 + - "𝔦": [t: "DL⠊"] # 0x1d526 + - "𝔧": [t: "DL⠚"] # 0x1d527 + - "𝔨": [t: "DL⠅"] # 0x1d528 + - "𝔩": [t: "DL⠇"] # 0x1d529 + - "𝔪": [t: "DL⠍"] # 0x1d52a + - "𝔫": [t: "DL⠝"] # 0x1d52b + - "𝔬": [t: "DL⠕"] # 0x1d52c + - "𝔭": [t: "DL⠏"] # 0x1d52d + - "𝔮": [t: "DL⠟"] # 0x1d52e + - "𝔯": [t: "DL⠗"] # 0x1d52f + - "𝔰": [t: "DL⠎"] # 0x1d530 + - "𝔱": [t: "DL⠞"] # 0x1d531 + - "𝔲": [t: "DL⠥"] # 0x1d532 + - "𝔳": [t: "DL⠧"] # 0x1d533 + - "𝔴": [t: "DL⠺"] # 0x1d534 + - "𝔵": [t: "DL⠭"] # 0x1d535 + - "𝔶": [t: "DL⠽"] # 0x1d536 + - "𝔷": [t: "DL⠵"] # 0x1d537 + + - "𝔄": [t: "DCL⠁"] # 0x1d504 + - "𝔅": [t: "DCL⠃"] # 0x1d505 + - "𝔆": [t: "DCL⠉"] # 0x1d506 + - "𝔇": [t: "DCL⠙"] # 0x1d507 + - "𝔈": [t: "DCL⠑"] # 0x1d508 + - "𝔉": [t: "DCL⠋"] # 0x1d509 + - "𝔊": [t: "DCL⠛"] # 0x1d50a + - "𝔋": [t: "DCL⠓"] # 0x1d50b + - "𝔌": [t: "DCL⠊"] # 0x1d50c + - "𝔍": [t: "DCL⠚"] # 0x1d50d + - "𝔎": [t: "DCL⠅"] # 0x1d50e + - "𝔏": [t: "DCL⠇"] # 0x1d50f + - "𝔐": [t: "DCL⠍"] # 0x1d510 + - "𝔑": [t: "DCL⠝"] # 0x1d511 + - "𝔒": [t: "DCL⠕"] # 0x1d512 + - "𝔓": [t: "DCL⠏"] # 0x1d513 + - "𝔔": [t: "DCL⠟"] # 0x1d514 + - "𝔕": [t: "DCL⠗"] # 0x1d515 + - "𝔖": [t: "DCL⠎"] # 0x1d516 + - "𝔗": [t: "DCL⠞"] # 0x1d517 + - "𝔘": [t: "DCL⠥"] # 0x1d518 + - "𝔙": [t: "DCL⠧"] # 0x1d519 + - "𝔚": [t: "DCL⠺"] # 0x1d51a + - "𝔛": [t: "DCL⠭"] # 0x1d51b + - "𝔜": [t: "DCL⠽"] # 0x1d51c + - "𝔝": [t: "DCL⠵"] # 0x1d51d + + - "ℌ": [t: "DCL⠓"] # 0x210C (Fraktur Capital H) + - "ℑ": [t: "DCL⠊"] # 0x2111 (Fraktur Capital I) + - "ℜ": [t: "DCL⠗"] # 0x211C (Fraktur Capital R) + - "ℨ": [t: "DCL⠵"] # 0x2128 (Fraktur Capital Z) + - "ℭ": [t: "DCL⠉"] # 0x22DC (Fraktur Capital C) + - "𝕒": [t: "𝔹L⠁"] # 0x1d552 + - "𝕓": [t: "𝔹L⠃"] # 0x1d553 + - "𝕔": [t: "𝔹L⠉"] # 0x1d554 + - "𝕕": [t: "𝔹L⠙"] # 0x1d555 + - "𝕖": [t: "𝔹L⠑"] # 0x1d556 + - "𝕗": [t: "𝔹L⠋"] # 0x1d557 + - "𝕘": [t: "𝔹L⠛"] # 0x1d558 + - "𝕙": [t: "𝔹L⠓"] # 0x1d559 + - "𝕚": [t: "𝔹L⠊"] # 0x1d55a + - "𝕛": [t: "𝔹L⠚"] # 0x1d55b + - "𝕜": [t: "𝔹L⠅"] # 0x1d55c + - "𝕝": [t: "𝔹L⠇"] # 0x1d55d + - "𝕞": [t: "𝔹L⠍"] # 0x1d55e + - "𝕟": [t: "𝔹L⠝"] # 0x1d55f + - "𝕠": [t: "𝔹L⠕"] # 0x1d560 + - "𝕡": [t: "𝔹L⠏"] # 0x1d561 + - "𝕢": [t: "𝔹L⠟"] # 0x1d562 + - "𝕣": [t: "𝔹L⠗"] # 0x1d563 + - "𝕤": [t: "𝔹L⠎"] # 0x1d564 + - "𝕥": [t: "𝔹L⠞"] # 0x1d565 + - "𝕦": [t: "𝔹L⠥"] # 0x1d566 + - "𝕧": [t: "𝔹L⠧"] # 0x1d567 + - "𝕨": [t: "𝔹L⠺"] # 0x1d568 + - "𝕩": [t: "𝔹L⠭"] # 0x1d569 + - "𝕪": [t: "𝔹L⠽"] # 0x1d56a + - "𝕫": [t: "𝔹L⠵"] # 0x1d56b + + - "𝔸": [t: "𝔹CL⠁"] # 0x1d538 + - "𝔹": [t: "𝔹CL⠃"] # 0x1d539 + - "ℂ": [t: "𝔹CL⠉"] # 0x2102 + - "𝔻": [t: "𝔹CL⠙"] # 0x1d53b + - "𝔼": [t: "𝔹CL⠑"] # 0x1d53c + - "𝔽": [t: "𝔹CL⠋"] # 0x1d53d + - "𝔾": [t: "𝔹CL⠛"] # 0x1d53e + - "ℍ": [t: "𝔹CL⠓"] # 0x210d + - "𝕀": [t: "𝔹CL⠊"] # 0x1d540 + - "𝕁": [t: "𝔹CL⠚"] # 0x1d541 + - "𝕂": [t: "𝔹CL⠅"] # 0x1d542 + - "𝕃": [t: "𝔹CL⠇"] # 0x1d543 + - "𝕄": [t: "𝔹CL⠍"] # 0x1d544 + - "ℕ": [t: "𝔹CL⠝"] # 0x2115 + - "𝕆": [t: "𝔹CL⠕"] # 0x1d546 + - "ℙ": [t: "𝔹CL⠏"] # 0x2119 + - "ℚ": [t: "𝔹CL⠟"] # 0x211a + - "ℝ": [t: "𝔹CL⠗"] # 0x211d + - "𝕊": [t: "𝔹CL⠎"] # 0x1d54a + - "𝕋": [t: "𝔹CL⠞"] # 0x1d54b + - "𝕌": [t: "𝔹CL⠥"] # 0x1d54c + - "𝕍": [t: "𝔹CL⠧"] # 0x1d54d + - "𝕎": [t: "𝔹CL⠺"] # 0x1d54e + - "𝕏": [t: "𝔹CL⠭"] # 0x1d54f + - "𝕐": [t: "𝔹CL⠽"] # 0x1d550 + - "ℤ": [t: "𝔹CL⠵"] # 0x2124 + - "𝖆": [t: "BDL⠁"] # 0x1d586 + - "𝖇": [t: "BDL⠃"] # 0x1d587 + - "𝖈": [t: "BDL⠉"] # 0x1d588 + - "𝖉": [t: "BDL⠙"] # 0x1d589 + - "𝖊": [t: "BDL⠑"] # 0x1d58a + - "𝖋": [t: "BDL⠋"] # 0x1d58b + - "𝖌": [t: "BDL⠛"] # 0x1d58c + - "𝖍": [t: "BDL⠓"] # 0x1d58d + - "𝖎": [t: "BDL⠊"] # 0x1d58e + - "𝖏": [t: "BDL⠚"] # 0x1d58f + - "𝖐": [t: "BDL⠅"] # 0x1d590 + - "𝖑": [t: "BDL⠇"] # 0x1d591 + - "𝖒": [t: "BDL⠍"] # 0x1d592 + - "𝖓": [t: "BDL⠝"] # 0x1d593 + - "𝖔": [t: "BDL⠕"] # 0x1d594 + - "𝖕": [t: "BDL⠏"] # 0x1d595 + - "𝖖": [t: "BDL⠟"] # 0x1d596 + - "𝖗": [t: "BDL⠗"] # 0x1d597 + - "𝖘": [t: "BDL⠎"] # 0x1d598 + - "𝖙": [t: "BDL⠞"] # 0x1d599 + - "𝖚": [t: "BDL⠥"] # 0x1d59a + - "𝖛": [t: "BDL⠧"] # 0x1d59b + - "𝖜": [t: "BDL⠺"] # 0x1d59c + - "𝖝": [t: "BDL⠭"] # 0x1d59d + - "𝖞": [t: "BDL⠽"] # 0x1d59e + - "𝖟": [t: "BDL⠵"] # 0x1d59f + + - "𝕬": [t: "BDCL⠁"] # 0x1d56c + - "𝕭": [t: "BDCL⠃"] # 0x1d56d + - "𝕮": [t: "BDCL⠉"] # 0x1d56e + - "𝕯": [t: "BDCL⠙"] # 0x1d56f + - "𝕰": [t: "BDCL⠑"] # 0x1d570 + - "𝕱": [t: "BDCL⠋"] # 0x1d571 + - "𝕲": [t: "BDCL⠛"] # 0x1d572 + - "𝕳": [t: "BDCL⠓"] # 0x1d573 + - "𝕴": [t: "BDCL⠊"] # 0x1d574 + - "𝕵": [t: "BDCL⠚"] # 0x1d575 + - "𝕶": [t: "BDCL⠅"] # 0x1d576 + - "𝕷": [t: "BDCL⠇"] # 0x1d577 + - "𝕸": [t: "BDCL⠍"] # 0x1d578 + - "𝕹": [t: "BDCL⠝"] # 0x1d579 + - "𝕺": [t: "BDCL⠕"] # 0x1d57a + - "𝕻": [t: "BDCL⠏"] # 0x1d57b + - "𝕼": [t: "BDCL⠟"] # 0x1d57c + - "𝕽": [t: "BDCL⠗"] # 0x1d57d + - "𝕾": [t: "BDCL⠎"] # 0x1d57e + - "𝕿": [t: "BDCL⠞"] # 0x1d57f + - "𝖀": [t: "BDCL⠥"] # 0x1d580 + - "𝖁": [t: "BDCL⠧"] # 0x1d581 + - "𝖂": [t: "BDCL⠺"] # 0x1d582 + - "𝖃": [t: "BDCL⠭"] # 0x1d583 + - "𝖄": [t: "BDCL⠽"] # 0x1d584 + - "𝖅": [t: "BDCL⠵"] # 0x1d585 + + - "𝖺": [t: "SL⠁"] # 0x1d5ba + - "𝖻": [t: "SL⠃"] # 0x1d5bb + - "𝖼": [t: "SL⠉"] # 0x1d5bc + - "𝖽": [t: "SL⠙"] # 0x1d5bd + - "𝖾": [t: "SL⠑"] # 0x1d5be + - "𝖿": [t: "SL⠋"] # 0x1d5bf + - "𝗀": [t: "SL⠛"] # 0x1d5c0 + - "𝗁": [t: "SL⠓"] # 0x1d5c1 + - "𝗂": [t: "SL⠊"] # 0x1d5c2 + - "𝗃": [t: "SL⠚"] # 0x1d5c3 + - "𝗄": [t: "SL⠅"] # 0x1d5c4 + - "𝗅": [t: "SL⠇"] # 0x1d5c5 + - "𝗆": [t: "SL⠍"] # 0x1d5c6 + - "𝗇": [t: "SL⠝"] # 0x1d5c7 + - "𝗈": [t: "SL⠕"] # 0x1d5c8 + - "𝗉": [t: "SL⠏"] # 0x1d5c9 + - "𝗊": [t: "SL⠟"] # 0x1d5ca + - "𝗋": [t: "SL⠗"] # 0x1d5cb + - "𝗌": [t: "SL⠎"] # 0x1d5cc + - "𝗍": [t: "SL⠞"] # 0x1d5cd + - "𝗎": [t: "SL⠥"] # 0x1d5ce + - "𝗏": [t: "SL⠧"] # 0x1d5cf + - "𝗐": [t: "SL⠺"] # 0x1d5d0 + - "𝗑": [t: "SL⠭"] # 0x1d5d1 + - "𝗒": [t: "SL⠽"] # 0x1d5d2 + - "𝗓": [t: "SL⠵"] # 0x1d5d3 + + - "𝖠": [t: "SCL⠁"] # 0x1d5a0 + - "𝖡": [t: "SCL⠃"] # 0x1d5a1 + - "𝖢": [t: "SCL⠉"] # 0x1d5a2 + - "𝖣": [t: "SCL⠙"] # 0x1d5a3 + - "𝖤": [t: "SCL⠑"] # 0x1d5a4 + - "𝖥": [t: "SCL⠋"] # 0x1d5a5 + - "𝖦": [t: "SCL⠛"] # 0x1d5a6 + - "𝖧": [t: "SCL⠓"] # 0x1d5a7 + - "𝖨": [t: "SCL⠊"] # 0x1d5a8 + - "𝖩": [t: "SCL⠚"] # 0x1d5a9 + - "𝖪": [t: "SCL⠅"] # 0x1d5aa + - "𝖫": [t: "SCL⠇"] # 0x1d5ab + - "𝖬": [t: "SCL⠍"] # 0x1d5ac + - "𝖭": [t: "SCL⠝"] # 0x1d5ad + - "𝖮": [t: "SCL⠕"] # 0x1d5ae + - "𝖯": [t: "SCL⠏"] # 0x1d5af + - "𝖰": [t: "SCL⠟"] # 0x1d5b0 + - "𝖱": [t: "SCL⠗"] # 0x1d5b1 + - "𝖲": [t: "SCL⠎"] # 0x1d5b2 + - "𝖳": [t: "SCL⠞"] # 0x1d5b3 + - "𝖴": [t: "SCL⠥"] # 0x1d5b4 + - "𝖵": [t: "SCL⠧"] # 0x1d5b5 + - "𝖶": [t: "SCL⠺"] # 0x1d5b6 + - "𝖷": [t: "SCL⠭"] # 0x1d5b7 + - "𝖸": [t: "SCL⠽"] # 0x1d5b8 + - "𝖹": [t: "SCL⠵"] # 0x1d5b9 + + - "𝗮": [t: "BSL⠁"] # 0x1d5ee + - "𝗯": [t: "BSL⠃"] # 0x1d5ef + - "𝗰": [t: "BSL⠉"] # 0x1d5f0 + - "𝗱": [t: "BSL⠙"] # 0x1d5f1 + - "𝗲": [t: "BSL⠑"] # 0x1d5f2 + - "𝗳": [t: "BSL⠋"] # 0x1d5f3 + - "𝗴": [t: "BSL⠛"] # 0x1d5f4 + - "𝗵": [t: "BSL⠓"] # 0x1d5f5 + - "𝗶": [t: "BSL⠊"] # 0x1d5f6 + - "𝗷": [t: "BSL⠚"] # 0x1d5f7 + - "𝗸": [t: "BSL⠅"] # 0x1d5f8 + - "𝗹": [t: "BSL⠇"] # 0x1d5f9 + - "𝗺": [t: "BSL⠍"] # 0x1d5fa + - "𝗻": [t: "BSL⠝"] # 0x1d5fb + - "𝗼": [t: "BSL⠕"] # 0x1d5fc + - "𝗽": [t: "BSL⠏"] # 0x1d5fd + - "𝗾": [t: "BSL⠟"] # 0x1d5fe + - "𝗿": [t: "BSL⠗"] # 0x1d5ff + - "𝘀": [t: "BSL⠎"] # 0x1d600 + - "𝘁": [t: "BSL⠞"] # 0x1d601 + - "𝘂": [t: "BSL⠥"] # 0x1d602 + - "𝘃": [t: "BSL⠧"] # 0x1d603 + - "𝘄": [t: "BSL⠺"] # 0x1d604 + - "𝘅": [t: "BSL⠭"] # 0x1d605 + - "𝘆": [t: "BSL⠽"] # 0x1d606 + - "𝘇": [t: "BSL⠵"] # 0x1d607 + + - "𝗔": [t: "BSCL⠁"] # 0x1d5d4 + - "𝗕": [t: "BSCL⠃"] # 0x1d5d5 + - "𝗖": [t: "BSCL⠉"] # 0x1d5d6 + - "𝗗": [t: "BSCL⠙"] # 0x1d5d7 + - "𝗘": [t: "BSCL⠑"] # 0x1d5d8 + - "𝗙": [t: "BSCL⠋"] # 0x1d5d9 + - "𝗚": [t: "BSCL⠛"] # 0x1d5da + - "𝗛": [t: "BSCL⠓"] # 0x1d5db + - "𝗜": [t: "BSCL⠊"] # 0x1d5dc + - "𝗝": [t: "BSCL⠚"] # 0x1d5dd + - "𝗞": [t: "BSCL⠅"] # 0x1d5de + - "𝗟": [t: "BSCL⠇"] # 0x1d5df + - "𝗠": [t: "BSCL⠍"] # 0x1d5e0 + - "𝗡": [t: "BSCL⠝"] # 0x1d5e1 + - "𝗢": [t: "BSCL⠕"] # 0x1d5e2 + - "𝗣": [t: "BSCL⠏"] # 0x1d5e3 + - "𝗤": [t: "BSCL⠟"] # 0x1d5e4 + - "𝗥": [t: "BSCL⠗"] # 0x1d5e5 + - "𝗦": [t: "BSCL⠎"] # 0x1d5e6 + - "𝗧": [t: "BSCL⠞"] # 0x1d5e7 + - "𝗨": [t: "BSCL⠥"] # 0x1d5e8 + - "𝗩": [t: "BSCL⠧"] # 0x1d5e9 + - "𝗪": [t: "BSCL⠺"] # 0x1d5ea + - "𝗫": [t: "BSCL⠭"] # 0x1d5eb + - "𝗬": [t: "BSCL⠽"] # 0x1d5ec + - "𝗭": [t: "BSCL⠵"] # 0x1d5ed + + - "𝘢": [t: "ISL⠁"] # 0x1d622 + - "𝘣": [t: "ISL⠃"] # 0x1d623 + - "𝘤": [t: "ISL⠉"] # 0x1d624 + - "𝘥": [t: "ISL⠙"] # 0x1d625 + - "𝘦": [t: "ISL⠑"] # 0x1d626 + - "𝘧": [t: "ISL⠋"] # 0x1d627 + - "𝘨": [t: "ISL⠛"] # 0x1d628 + - "𝘩": [t: "ISL⠓"] # 0x1d629 + - "𝘪": [t: "ISL⠊"] # 0x1d62a + - "𝘫": [t: "ISL⠚"] # 0x1d62b + - "𝘬": [t: "ISL⠅"] # 0x1d62c + - "𝘭": [t: "ISL⠇"] # 0x1d62d + - "𝘮": [t: "ISL⠍"] # 0x1d62e + - "𝘯": [t: "ISL⠝"] # 0x1d62f + - "𝘰": [t: "ISL⠕"] # 0x1d630 + - "𝘱": [t: "ISL⠏"] # 0x1d631 + - "𝘲": [t: "ISL⠟"] # 0x1d632 + - "𝘳": [t: "ISL⠗"] # 0x1d633 + - "𝘴": [t: "ISL⠎"] # 0x1d634 + - "𝘵": [t: "ISL⠞"] # 0x1d635 + - "𝘶": [t: "ISL⠥"] # 0x1d636 + - "𝘷": [t: "ISL⠧"] # 0x1d637 + - "𝘸": [t: "ISL⠺"] # 0x1d638 + - "𝘹": [t: "ISL⠭"] # 0x1d639 + - "𝘺": [t: "ISL⠽"] # 0x1d63a + - "𝘻": [t: "ISL⠵"] # 0x1d63b + + - "𝘈": [t: "ISCL⠁"] # 0x1d608 + - "𝘉": [t: "ISCL⠃"] # 0x1d609 + - "𝘊": [t: "ISCL⠉"] # 0x1d60a + - "𝘋": [t: "ISCL⠙"] # 0x1d60b + - "𝘌": [t: "ISCL⠑"] # 0x1d60c + - "𝘍": [t: "ISCL⠋"] # 0x1d60d + - "𝘎": [t: "ISCL⠛"] # 0x1d60e + - "𝘏": [t: "ISCL⠓"] # 0x1d60f + - "𝘐": [t: "ISCL⠊"] # 0x1d610 + - "𝘑": [t: "ISCL⠚"] # 0x1d611 + - "𝘒": [t: "ISCL⠅"] # 0x1d612 + - "𝘓": [t: "ISCL⠇"] # 0x1d613 + - "𝘔": [t: "ISCL⠍"] # 0x1d614 + - "𝘕": [t: "ISCL⠝"] # 0x1d615 + - "𝘖": [t: "ISCL⠕"] # 0x1d616 + - "𝘗": [t: "ISCL⠏"] # 0x1d617 + - "𝘘": [t: "ISCL⠟"] # 0x1d618 + - "𝘙": [t: "ISCL⠗"] # 0x1d619 + - "𝘚": [t: "ISCL⠎"] # 0x1d61a + - "𝘛": [t: "ISCL⠞"] # 0x1d61b + - "𝘜": [t: "ISCL⠥"] # 0x1d61c + - "𝘝": [t: "ISCL⠧"] # 0x1d61d + - "𝘞": [t: "ISCL⠺"] # 0x1d61e + - "𝘟": [t: "ISCL⠭"] # 0x1d61f + - "𝘠": [t: "ISCL⠽"] # 0x1d620 + - "𝘡": [t: "ISCL⠵"] # 0x1d621 + + - "𝙖": [t: "BILS⠁"] # 0x1d656 + - "𝙗": [t: "BILS⠃"] # 0x1d657 + - "𝙘": [t: "BILS⠉"] # 0x1d658 + - "𝙙": [t: "BILS⠙"] # 0x1d659 + - "𝙚": [t: "BILS⠑"] # 0x1d65a + - "𝙛": [t: "BILS⠋"] # 0x1d65b + - "𝙜": [t: "BILS⠛"] # 0x1d65c + - "𝙝": [t: "BILS⠓"] # 0x1d65d + - "𝙞": [t: "BILS⠊"] # 0x1d65e + - "𝙟": [t: "BILS⠚"] # 0x1d65f + - "𝙠": [t: "BILS⠅"] # 0x1d660 + - "𝙡": [t: "BILS⠇"] # 0x1d661 + - "𝙢": [t: "BILS⠍"] # 0x1d662 + - "𝙣": [t: "BILS⠝"] # 0x1d663 + - "𝙤": [t: "BILS⠕"] # 0x1d664 + - "𝙥": [t: "BILS⠏"] # 0x1d665 + - "𝙦": [t: "BILS⠟"] # 0x1d666 + - "𝙧": [t: "BILS⠗"] # 0x1d667 + - "𝙨": [t: "BILS⠎"] # 0x1d668 + - "𝙩": [t: "BILS⠞"] # 0x1d669 + - "𝙪": [t: "BILS⠥"] # 0x1d66a + - "𝙫": [t: "BILS⠧"] # 0x1d66b + - "𝙬": [t: "BILS⠺"] # 0x1d66c + - "𝙭": [t: "BILS⠭"] # 0x1d66d + - "𝙮": [t: "BILS⠽"] # 0x1d66e + - "𝙯": [t: "BILS⠵"] # 0x1d66f + + - "𝘼": [t: "BILSC⠁"] # 0x1d63c + - "𝘽": [t: "BILSC⠃"] # 0x1d63d + - "𝘾": [t: "BILSC⠉"] # 0x1d63e + - "𝘿": [t: "BILSC⠙"] # 0x1d63f + - "𝙀": [t: "BILSC⠑"] # 0x1d640 + - "𝙁": [t: "BILSC⠋"] # 0x1d641 + - "𝙂": [t: "BILSC⠛"] # 0x1d642 + - "𝙃": [t: "BILSC⠓"] # 0x1d643 + - "𝙄": [t: "BILSC⠊"] # 0x1d644 + - "𝙅": [t: "BILSC⠚"] # 0x1d645 + - "𝙆": [t: "BILSC⠅"] # 0x1d646 + - "𝙇": [t: "BILSC⠇"] # 0x1d647 + - "𝙈": [t: "BILSC⠍"] # 0x1d648 + - "𝙉": [t: "BILSC⠝"] # 0x1d649 + - "𝙊": [t: "BILSC⠕"] # 0x1d64a + - "𝙋": [t: "BILSC⠏"] # 0x1d64b + - "𝙌": [t: "BILSC⠟"] # 0x1d64c + - "𝙍": [t: "BILSC⠗"] # 0x1d64d + - "𝙎": [t: "BILSC⠎"] # 0x1d64e + - "𝙏": [t: "BILSC⠞"] # 0x1d64f + - "𝙐": [t: "BILSC⠥"] # 0x1d650 + - "𝙑": [t: "BILSC⠧"] # 0x1d651 + - "𝙒": [t: "BILSC⠺"] # 0x1d652 + - "𝙓": [t: "BILSC⠭"] # 0x1d653 + - "𝙔": [t: "BILSC⠽"] # 0x1d654 + - "𝙕": [t: "BILSC⠵"] # 0x1d655 + + - "𝚊": [t: "L⠁"] # 0x1d68a + - "𝚋": [t: "L⠃"] # 0x1d68b + - "𝚌": [t: "L⠉"] # 0x1d68c + - "𝚍": [t: "L⠙"] # 0x1d68d + - "𝚎": [t: "L⠑"] # 0x1d68e + - "𝚏": [t: "L⠋"] # 0x1d68f + - "𝚐": [t: "L⠛"] # 0x1d690 + - "𝚑": [t: "L⠓"] # 0x1d691 + - "𝚒": [t: "L⠊"] # 0x1d692 + - "𝚓": [t: "L⠚"] # 0x1d693 + - "𝚔": [t: "L⠅"] # 0x1d694 + - "𝚕": [t: "L⠇"] # 0x1d695 + - "𝚖": [t: "L⠍"] # 0x1d696 + - "𝚗": [t: "L⠝"] # 0x1d697 + - "𝚘": [t: "L⠕"] # 0x1d698 + - "𝚙": [t: "L⠏"] # 0x1d699 + - "𝚚": [t: "L⠟"] # 0x1d69a + - "𝚛": [t: "L⠗"] # 0x1d69b + - "𝚜": [t: "L⠎"] # 0x1d69c + - "𝚝": [t: "L⠞"] # 0x1d69d + - "𝚞": [t: "L⠥"] # 0x1d69e + - "𝚟": [t: "L⠧"] # 0x1d69f + - "𝚠": [t: "L⠺"] # 0x1d6a0 + - "𝚡": [t: "L⠭"] # 0x1d6a1 + - "𝚢": [t: "L⠽"] # 0x1d6a2 + - "𝚣": [t: "L⠵"] # 0x1d6a3 + + - "𝙰": [t: "CL⠁"] # 0x1d670 + - "𝙱": [t: "CL⠃"] # 0x1d671 + - "𝙲": [t: "CL⠉"] # 0x1d672 + - "𝙳": [t: "CL⠙"] # 0x1d673 + - "𝙴": [t: "CL⠑"] # 0x1d674 + - "𝙵": [t: "CL⠋"] # 0x1d675 + - "𝙶": [t: "CL⠛"] # 0x1d676 + - "𝙷": [t: "CL⠓"] # 0x1d677 + - "𝙸": [t: "CL⠊"] # 0x1d678 + - "𝙹": [t: "CL⠚"] # 0x1d679 + - "𝙺": [t: "CL⠅"] # 0x1d67a + - "𝙻": [t: "CL⠇"] # 0x1d67b + - "𝙼": [t: "CL⠍"] # 0x1d67c + - "𝙽": [t: "CL⠝"] # 0x1d67d + - "𝙾": [t: "CL⠕"] # 0x1d67e + - "𝙿": [t: "CL⠏"] # 0x1d67f + - "𝚀": [t: "CL⠟"] # 0x1d680 + - "𝚁": [t: "CL⠗"] # 0x1d681 + - "𝚂": [t: "CL⠎"] # 0x1d682 + - "𝚃": [t: "CL⠞"] # 0x1d683 + - "𝚄": [t: "CL⠥"] # 0x1d684 + - "𝚅": [t: "CL⠧"] # 0x1d685 + - "𝚆": [t: "CL⠺"] # 0x1d686 + - "𝚇": [t: "CL⠭"] # 0x1d687 + - "𝚈": [t: "CL⠽"] # 0x1d688 + - "𝚉": [t: "CL⠵"] # 0x1d689 + + - "𝚤": [t: "L⠊"] # 0x1d6a4 (dotless i) + - "𝚥": [t: "L⠚"] # 0x1d6a5 (dotless j) + - "𝛂": [t: "BGL⠁"] # 0x1d6c2 + - "𝛃": [t: "BGL⠃"] # 0x1d6c3 + - "𝛄": [t: "BGL⠛"] # 0x1d6c4 + - "𝛅": [t: "BGL⠙"] # 0x1d6c5 + - "𝛆": [t: "BGL⠑"] # 0x1d6c6 + - "𝛇": [t: "BGL⠵"] # 0x1d6c7 + - "𝛈": [t: "BGL⠱ "] # 0x1d6c8 + - "𝛉": [t: "BGL⠹"] # 0x1d6c9 + - "𝛊": [t: "BGL⠊"] # 0x1d6ca + - "𝛋": [t: "BGL⠅"] # 0x1d6cb + - "𝛌": [t: "BGL⠇"] # 0x1d6cc + - "𝛍": [t: "BGL⠍"] # 0x1d6cd + - "𝛎": [t: "BGL⠝"] # 0x1d6ce + - "𝛏": [t: "BGL⠭"] # 0x1d6cf + - "𝛐": [t: "BGL⠕"] # 0x1d6d0 + - "𝛑": [t: "BGL⠏"] # 0x1d6d1 + - "𝛒": [t: "BGL⠗"] # 0x1d6d2 + - "𝛓": [t: "BGL⠎"] # 0x1d6d3 + - "𝛔": [t: "BGL⠞"] # 0x1d6d4 + - "𝛕": [t: "BGL⠥"] # 0x1d6d5 + - "𝛖": [t: "BGL⠋"] # 0x1d6d6 + - "𝛗": [t: "BGL⠯"] # 0x1d6d7 + - "𝛘": [t: "BGL⠽"] # 0x1d6d8 + - "𝛙": [t: "BGL⠺"] # 0x1d6d9 + + - "𝚨": [t: "BCGL⠁"] # 0x1d6a8 + - "𝚩": [t: "BCGL⠃"] # 0x1d6a9 + - "𝚪": [t: "BCGL⠛"] # 0x1d6aa + - "𝚫": [t: "BCGL⠙"] # 0x1d6ab + - "𝚬": [t: "BCGL⠑"] # 0x1d6ac + - "𝚭": [t: "BCGL⠵"] # 0x1d6ad + - "𝚮": [t: "BCGL⠱ "] # 0x1d6ae + - "𝚯": [t: "BCGL⠹"] # 0x1d6af + - "𝚰": [t: "BCGL⠊"] # 0x1d6b0 + - "𝚱": [t: "BCGL⠅"] # 0x1d6b1 + - "𝚲": [t: "BCGL⠇"] # 0x1d6b2 + - "𝚳": [t: "BCGL⠍"] # 0x1d6b3 + - "𝚴": [t: "BCGL⠝"] # 0x1d6b4 + - "𝚵": [t: "BCGL⠭"] # 0x1d6b5 + - "𝚶": [t: "BCGL⠕"] # 0x1d6b6 + - "𝚷": [t: "BCGL⠏"] # 0x1d6b7 + - "𝚸": [t: "BCGL⠗"] # 0x1d6b8 + - "𝚹": [t: "BCGL⠎"] # 0x1d6b9 + - "𝚺": [t: "BCGL⠞"] # 0x1d6ba + - "𝚻": [t: "BCGL⠥"] # 0x1d6bb + - "𝚼": [t: "BCGL⠋"] # 0x1d6bc + - "𝚽": [t: "BCGL⠯"] # 0x1d6bd + - "𝚾": [t: "BCGL⠽"] # 0x1d6be + - "𝚿": [t: "BCGL⠺"] # 0x1d6bf + + - "𝛼": [t: "IGL⠁"] # 0x1d6fc + - "𝛽": [t: "IGL⠃"] # 0x1d6fd + - "𝛾": [t: "IGL⠛"] # 0x1d6fe + - "𝛿": [t: "IGL⠙"] # 0x1d6ff + - "𝜀": [t: "IGL⠑"] # 0x1d700 + - "𝜁": [t: "IGL⠵"] # 0x1d701 + - "𝜂": [t: "IGL⠱ "] # 0x1d702 + - "𝜃": [t: "IGL⠹"] # 0x1d703 + - "𝜄": [t: "IGL⠊"] # 0x1d704 + - "𝜅": [t: "IGL⠅"] # 0x1d705 + - "𝜆": [t: "IGL⠇"] # 0x1d706 + - "𝜇": [t: "IGL⠍"] # 0x1d707 + - "𝜈": [t: "IGL⠝"] # 0x1d708 + - "𝜉": [t: "IGL⠭"] # 0x1d709 + - "𝜊": [t: "IGL⠕"] # 0x1d70a + - "𝜋": [t: "IGL⠏"] # 0x1d70b + - "𝜌": [t: "IGL⠗"] # 0x1d70c + - "𝜍": [t: "IGL⠎"] # 0x1d70d + - "𝜎": [t: "IGL⠞"] # 0x1d70e + - "𝜏": [t: "IGL⠥"] # 0x1d70f + - "𝜐": [t: "IGL⠋"] # 0x1d710 + - "𝜑": [t: "IGL⠯"] # 0x1d711 + - "𝜒": [t: "IGL⠽"] # 0x1d712 + - "𝜓": [t: "IGL⠺"] # 0x1d713 + + - "𝛢": [t: "ICGL⠁"] # 0x1d6e2 + - "𝛣": [t: "ICGL⠃"] # 0x1d6e3 + - "𝛤": [t: "ICGL⠛"] # 0x1d6e4 + - "𝛥": [t: "ICGL⠙"] # 0x1d6e5 + - "𝛦": [t: "ICGL⠑"] # 0x1d6e6 + - "𝛧": [t: "ICGL⠵"] # 0x1d6e7 + - "𝛨": [t: "ICGL⠱ "] # 0x1d6e8 + - "𝛩": [t: "ICGL⠹"] # 0x1d6e9 + - "𝛪": [t: "ICGL⠊"] # 0x1d6ea + - "𝛫": [t: "ICGL⠅"] # 0x1d6eb + - "𝛬": [t: "ICGL⠇"] # 0x1d6ec + - "𝛭": [t: "ICGL⠍"] # 0x1d6ed + - "𝛮": [t: "ICGL⠝"] # 0x1d6ee + - "𝛯": [t: "ICGL⠭"] # 0x1d6ef + - "𝛰": [t: "ICGL⠕"] # 0x1d6f0 + - "𝛱": [t: "ICGL⠏"] # 0x1d6f1 + - "𝛲": [t: "ICGL⠗"] # 0x1d6f2 + - "𝛳": [t: "ICGL⠎"] # 0x1d6f3 + - "𝛴": [t: "ICGL⠞"] # 0x1d6f4 + - "𝛵": [t: "ICGL⠥"] # 0x1d6f5 + - "𝛶": [t: "ICGL⠋"] # 0x1d6f6 + - "𝛷": [t: "ICGL⠯"] # 0x1d6f7 + - "𝛸": [t: "ICGL⠽"] # 0x1d6f8 + - "𝛹": [t: "ICGL⠺"] # 0x1d6f9 + + - "𝜶": [t: "BIGL⠁"] # 0x1d736 + - "𝜷": [t: "BIGL⠃"] # 0x1d737 + - "𝜸": [t: "BIGL⠛"] # 0x1d738 + - "𝜹": [t: "BIGL⠙"] # 0x1d739 + - "𝜺": [t: "BIGL⠑"] # 0x1d73a + - "𝜻": [t: "BIGL⠵"] # 0x1d73b + - "𝜼": [t: "BIGL⠱ "] # 0x1d73c + - "𝜽": [t: "BIGL⠹"] # 0x1d73d + - "𝜾": [t: "BIGL⠊"] # 0x1d73e + - "𝜿": [t: "BIGL⠅"] # 0x1d73f + - "𝝀": [t: "BIGL⠇"] # 0x1d740 + - "𝝁": [t: "BIGL⠍"] # 0x1d741 + - "𝝂": [t: "BIGL⠝"] # 0x1d742 + - "𝝃": [t: "BIGL⠭"] # 0x1d743 + - "𝝄": [t: "BIGL⠕"] # 0x1d744 + - "𝝅": [t: "BIGL⠏"] # 0x1d745 + - "𝝆": [t: "BIGL⠗"] # 0x1d746 + - "𝝇": [t: "BIGL⠎"] # 0x1d747 + - "𝝈": [t: "BIGL⠞"] # 0x1d748 + - "𝝉": [t: "BIGL⠥"] # 0x1d749 + - "𝝊": [t: "BIGL⠋"] # 0x1d74a + - "𝝋": [t: "BIGL⠯"] # 0x1d74b + - "𝝌": [t: "BIGL⠽"] # 0x1d74c + - "𝝍": [t: "BIGL⠺"] # 0x1d74d + + - "𝜜": [t: "BICGL⠁"] # 0x1d71c + - "𝜝": [t: "BICGL⠃"] # 0x1d71d + - "𝜞": [t: "BICGL⠛"] # 0x1d71e + - "𝜟": [t: "BICGL⠙"] # 0x1d71f + - "𝜠": [t: "BICGL⠑"] # 0x1d720 + - "𝜡": [t: "BICGL⠵"] # 0x1d721 + - "𝜢": [t: "BICGL⠱ "] # 0x1d722 + - "𝜣": [t: "BICGL⠹"] # 0x1d723 + - "𝜤": [t: "BICGL⠊"] # 0x1d724 + - "𝜥": [t: "BICGL⠅"] # 0x1d725 + - "𝜦": [t: "BICGL⠇"] # 0x1d726 + - "𝜧": [t: "BICGL⠍"] # 0x1d727 + - "𝜨": [t: "BICGL⠝"] # 0x1d728 + - "𝜩": [t: "BICGL⠭"] # 0x1d729 + - "𝜪": [t: "BICGL⠕"] # 0x1d72a + - "𝜫": [t: "BICGL⠏"] # 0x1d72b + - "𝜬": [t: "BICGL⠗"] # 0x1d72c + - "𝜭": [t: "BICGL⠎"] # 0x1d72d + - "𝜮": [t: "BICGL⠞"] # 0x1d72e + - "𝜯": [t: "BICGL⠥"] # 0x1d72f + - "𝜰": [t: "BICGL⠋"] # 0x1d730 + - "𝜱": [t: "BICGL⠯"] # 0x1d731 + - "𝜲": [t: "BICGL⠽"] # 0x1d732 + - "𝜳": [t: "BICGL⠺"] # 0x1d733 + + - "𝝰": [t: "BSGL⠁"] # 0x1d770 + - "𝝱": [t: "BSGL⠃"] # 0x1d771 + - "𝝲": [t: "BSGL⠛"] # 0x1d772 + - "𝝳": [t: "BSGL⠙"] # 0x1d773 + - "𝝴": [t: "BSGL⠑"] # 0x1d774 + - "𝝵": [t: "BSGL⠵"] # 0x1d775 + - "𝝶": [t: "BSGL⠱ "] # 0x1d776 + - "𝝷": [t: "BSGL⠹"] # 0x1d777 + - "𝝸": [t: "BSGL⠊"] # 0x1d778 + - "𝝹": [t: "BSGL⠅"] # 0x1d779 + - "𝝺": [t: "BSGL⠇"] # 0x1d77a + - "𝝻": [t: "BSGL⠍"] # 0x1d77b + - "𝝼": [t: "BSGL⠝"] # 0x1d77c + - "𝝽": [t: "BSGL⠭"] # 0x1d77d + - "𝝾": [t: "BSGL⠕"] # 0x1d77e + - "𝝿": [t: "BSGL⠏"] # 0x1d77f + - "𝞀": [t: "BSGL⠗"] # 0x1d780 + - "𝞁": [t: "BSGL⠎"] # 0x1d781 + - "𝞂": [t: "BSGL⠞"] # 0x1d782 + - "𝞃": [t: "BSGL⠥"] # 0x1d783 + - "𝞄": [t: "BSGL⠋"] # 0x1d784 + - "𝞅": [t: "BSGL⠯"] # 0x1d785 + - "𝞆": [t: "BSGL⠽"] # 0x1d786 + - "𝞇": [t: "BSGL⠺"] # 0x1d787 + + - "𝝖": [t: "BSCGL⠁"] # 0x1d756 + - "𝝗": [t: "BSCGL⠃"] # 0x1d757 + - "𝝘": [t: "BSCGL⠛"] # 0x1d758 + - "𝝙": [t: "BSCGL⠙"] # 0x1d759 + - "𝝚": [t: "BSCGL⠑"] # 0x1d75a + - "𝝛": [t: "BSCGL⠵"] # 0x1d75b + - "𝝜": [t: "BSCGL⠱ "] # 0x1d75c + - "𝝝": [t: "BSCGL⠹"] # 0x1d75d + - "𝝞": [t: "BSCGL⠊"] # 0x1d75e + - "𝝟": [t: "BSCGL⠅"] # 0x1d75f + - "𝝠": [t: "BSCGL⠇"] # 0x1d760 + - "𝝡": [t: "BSCGL⠍"] # 0x1d761 + - "𝝢": [t: "BSCGL⠝"] # 0x1d762 + - "𝝣": [t: "BSCGL⠭"] # 0x1d763 + - "𝝤": [t: "BSCGL⠕"] # 0x1d764 + - "𝝥": [t: "BSCGL⠏"] # 0x1d765 + - "𝝦": [t: "BSCGL⠗"] # 0x1d766 + - "𝝧": [t: "BSCGL⠎"] # 0x1d767 + - "𝝨": [t: "BSCGL⠞"] # 0x1d768 + - "𝝩": [t: "BSCGL⠥"] # 0x1d769 + - "𝝪": [t: "BSCGL⠋"] # 0x1d76a + - "𝝫": [t: "BSCGL⠯"] # 0x1d76b + - "𝝬": [t: "BSCGL⠽"] # 0x1d76c + - "𝝭": [t: "BSCGL⠺"] # 0x1d76d + + - "𝞪": [t: "BISGL⠁"] # 0x1d7aa + - "𝞫": [t: "BISGL⠃"] # 0x1d7ab + - "𝞬": [t: "BISGL⠛"] # 0x1d7ac + - "𝞭": [t: "BISGL⠙"] # 0x1d7ad + - "𝞮": [t: "BISGL⠑"] # 0x1d7ae + - "𝞯": [t: "BISGL⠵"] # 0x1d7af + - "𝞰": [t: "BISGL⠱ "] # 0x1d7b0 + - "𝞱": [t: "BISGL⠹"] # 0x1d7b1 + - "𝞲": [t: "BISGL⠊"] # 0x1d7b2 + - "𝞳": [t: "BISGL⠅"] # 0x1d7b3 + - "𝞴": [t: "BISGL⠇"] # 0x1d7b4 + - "𝞵": [t: "BISGL⠍"] # 0x1d7b5 + - "𝞶": [t: "BISGL⠝"] # 0x1d7b6 + - "𝞷": [t: "BISGL⠭"] # 0x1d7b7 + - "𝞸": [t: "BISGL⠕"] # 0x1d7b8 + - "𝞹": [t: "BISGL⠏"] # 0x1d7b9 + - "𝞺": [t: "BISGL⠗"] # 0x1d7ba + - "𝞻": [t: "BISGL⠎"] # 0x1d7bb + - "𝞼": [t: "BISGL⠞"] # 0x1d7bc + - "𝞽": [t: "BISGL⠥"] # 0x1d7bd + - "𝞾": [t: "BISGL⠋"] # 0x1d7be + - "𝞿": [t: "BISGL⠯"] # 0x1d7bf + - "𝟀": [t: "BISGL⠽"] # 0x1d7c0 + - "𝟁": [t: "BISGL⠺"] # 0x1d7c1 + + - "𝞐": [t: "BISCGL⠁"] # 0x1d790 + - "𝞑": [t: "BISCGL⠃"] # 0x1d791 + - "𝞒": [t: "BISCGL⠛"] # 0x1d792 + - "𝞓": [t: "BISCGL⠙"] # 0x1d793 + - "𝞔": [t: "BISCGL⠑"] # 0x1d794 + - "𝞕": [t: "BISCGL⠵"] # 0x1d795 + - "𝞖": [t: "BISCGL⠱ "] # 0x1d796 + - "𝞗": [t: "BISCGL⠹"] # 0x1d797 + - "𝞘": [t: "BISCGL⠊"] # 0x1d798 + - "𝞙": [t: "BISCGL⠅"] # 0x1d799 + - "𝞚": [t: "BISCGL⠇"] # 0x1d79a + - "𝞛": [t: "BISCGL⠍"] # 0x1d79b + - "𝞜": [t: "BISCGL⠝"] # 0x1d79c + - "𝞝": [t: "BISCGL⠭"] # 0x1d79d + - "𝞞": [t: "BISCGL⠕"] # 0x1d79e + - "𝞟": [t: "BISCGL⠏"] # 0x1d79f + - "𝞠": [t: "BISCGL⠗"] # 0x1d7a0 + - "𝞡": [t: "BISCGL⠎"] # 0x1d7a1 + - "𝞢": [t: "BISCGL⠞"] # 0x1d7a2 + - "𝞣": [t: "BISCGL⠥"] # 0x1d7a3 + - "𝞤": [t: "BISCGL⠋"] # 0x1d7a4 + - "𝞥": [t: "BISCGL⠯"] # 0x1d7a5 + - "𝞦": [t: "BISCGL⠽"] # 0x1d7a6 + - "𝞧": [t: "BISCGL⠺"] # 0x1d7a7 + + - "𝟎-𝟗": # 0x1d7ce - 0x1d7d7 + - t: "B" + - spell: "translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789')" + + - "-": # 0xf52e - 0xf537 (old MathType) + - t: "B" + - spell: "translate('.', '', '0123456789')" + + - "𝟬-𝟵": # 0x1D7EC - 0x1D7F5 + - t: "B" + - spell: "translate('.', '𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵', '0123456789')" + + - "-": # 0xf556 - 0xf55f (old MathType) + - t: "B" + - spell: "translate('.', '', '0123456789')" + + - "𝟢-𝟫": # 0x1d7e2 - 0x1d7eb + - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" + + - "𝟶-𝟿": # 0x1d7f6 - 0x1d7ff + - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" diff --git a/Rules/Braille/French/unicode.yaml b/Rules/Braille/French/unicode.yaml new file mode 100644 index 000000000..e37c31f40 --- /dev/null +++ b/Rules/Braille/French/unicode.yaml @@ -0,0 +1,317 @@ +--- +# French braille characters +# +# Some chars that originally came from an Excel spreadsheet +# +# The French translation makes use of some non-braille chars as flags +# L -- what follows is a letter +# N -- what follows is a digit +# C -- precedes L for capital letters +# G -- precedes L for Greek Letters (after C for capitals) +# Typeface Indicators ("typeforms") +# B bold, I Italic, S Sans-serif, T script, D, Fraktur, 𝔹 blackboard-bold +# the type face indicators can be combined with B (first) and I (second) +# Standing alone chars: +# Any character which acts as a standing alone character (e.g, "{") needs each braille symbol to be labeled +# o -- opening/left side, c -- closing/right side, b -- both open and close (e.g, '"') + + + + - "a": [t: "L⠁"] # 0x61 + - "b": [t: "L⠃"] # 0x62 + - "c": [t: "L⠉"] # 0x63 + - "d": [t: "L⠙"] # 0x64 + - "e": [t: "L⠑"] # 0x65 + - "f": [t: "L⠋"] # 0x66 + - "g": [t: "L⠛"] # 0x67 + - "h": [t: "L⠓"] # 0x68 + - "i": [t: "L⠊"] # 0x69 + - "j": [t: "L⠚"] # 0x6a + - "k": [t: "L⠅"] # 0x6b + - "l": [t: "L⠇"] # 0x6c + - "m": [t: "L⠍"] # 0x6d + - "n": [t: "L⠝"] # 0x6e + - "o": [t: "L⠕"] # 0x6f + - "p": [t: "L⠏"] # 0x70 + - "q": [t: "L⠟"] # 0x71 + - "r": [t: "L⠗"] # 0x72 + - "s": [t: "L⠎"] # 0x73 + - "t": [t: "L⠞"] # 0x74 + - "u": [t: "L⠥"] # 0x75 + - "v": [t: "L⠧"] # 0x76 + - "w": [t: "L⠺"] # 0x77 + - "x": [t: "L⠭"] # 0x78 + - "y": [t: "L⠽"] # 0x79 + - "z": [t: "L⠵"] # 0x7a + + - "A": [t: "CL⠁"] # 0x41 + - "B": # 0x42 + - test: + if: "@data-chem-element" + then: [t: "𝐶L⠃"] + else: [t: "CL⠃"] + - "C": # 0x43 + - test: + if: "@data-chem-element" + then: [t: "𝐶L⠉"] + else: [t: "CL⠉"] + - "D": [t: "CL⠙"] # 0x44 + - "E": [t: "CL⠑"] # 0x45 + - "F": [t: "CL⠋"] # 0x46 + - "G": [t: "CL⠛"] # 0x47 + - "H": # 0x48 + - test: + if: "@data-chem-element" + then: [t: "𝐶L⠓"] + else: [t: "CL⠓"] + - "I": # 0x49 + - test: + if: "@data-chem-element" + then: [t: "𝐶L⠊"] + else: [t: "CL⠊"] + - "J": [t: "CL⠚"] # 0x4a + - "K": # 0x4b + - test: + if: "@data-chem-element" + then: [t: "𝐶L⠅"] + else: [t: "CL⠅"] + - "L": [t: "CL⠇"] # 0x4c + - "M": [t: "CL⠍"] # 0x4d + - "N": # 0x4e + - test: + if: "@data-chem-element" + then: [t: "𝐶L⠝"] + else: [t: "CL⠝"] + - "O": # 0x4f + - test: + if: "@data-chem-element" + then: [t: "𝐶L⠕"] + else: [t: "CL⠕"] + - "P": # 0x50 + - test: + if: "@data-chem-element" + then: [t: "𝐶L⠏"] + else: [t: "CL⠏"] + - "Q": [t: "CL⠟"] # 0x51 + - "R": [t: "CL⠗"] # 0x52 + - "S": # 0x53 + - test: + if: "@data-chem-element" + then: [t: "𝐶L⠎"] + else: [t: "CL⠎"] + - "T": [t: "CL⠞"] # 0x54 + - "U": # 0x55 + - test: + if: "@data-chem-element" + then: [t: "𝐶L⠥"] + else: [t: "CL⠥"] + - "V": # 0x56 + - test: + if: "@data-chem-element" + then: [t: "𝐶L⠧"] + else: [t: "CL⠧"] + - "W": # 0x57 + - test: + if: "@data-chem-element" + then: [t: "𝐶L⠺"] + else: [t: "CL⠺"] + - "X": [t: "CL⠭"] # 0x58 + - "Y": # 0x59 + - test: + if: "@data-chem-element" + then: [t: "𝐶L⠽"] + else: [t: "CL⠽"] + - "Z": [t: "CL⠵"] # 0x5a + + - "α": [t: "GL⠁"] # 0x3b1 + - "β": [t: "GL⠃"] # 0x3b2 + - "γ": [t: "GL⠛"] # 0x3b3 + - "δ": [t: "GL⠙"] # 0x3b4 + - "ε": [t: "GL⠑"] # 0x3b5 + - "ζ": [t: "GL⠵"] # 0x3b6 + - "η": [t: "GL⠱"] # 0x3b7 + - "θ": [t: "GL⠹"] # 0x3b8 + - "ι": [t: "GL⠊"] # 0x3b9 + - "κ": [t: "GL⠅"] # 0x3ba + - "λ": [t: "GL⠇"] # 0x3bb + - "μ": [t: "GL⠍"] # 0x3bc + - "ν": [t: "GL⠝"] # 0x3bd + - "ξ": [t: "GL⠭"] # 0x3be + - "ο": [t: "GL⠕"] # 0x3bf + - "π": [t: "GL⠏"] # 0x3c0 + - "ρ": [t: "GL⠗"] # 0x3c1 + - "ς": [t: "GL⠎"] # 0x3c2 + - "σ": [t: "GL⠎"] # 0x3c3 + - "τ": [t: "GL⠞"] # 0x3c4 + - "υ": [t: "GL⠥"] # 0x3c5 + - "φ": [t: "GL⠋"] # 0x3c6 + - "χ": [t: "GL⠯"] # 0x3c7 + - "ψ": [t: "GL⠽"] # 0x3c8 + - "ω": [t: "GL⠺"] # 0x3c9 + + - "Α": [t: "CGL⠁"] # 0x391 + - "Β": [t: "CGL⠃"] # 0x392 + - "Γ": [t: "CGL⠛"] # 0x393 + - "Δ": [t: "CGL⠙"] # 0x394 + - "Ε": [t: "CGL⠑"] # 0x395 + - "Ζ": [t: "CGL⠵"] # 0x396 + - "Η": [t: "CGL⠱ "] # 0x397 + - "Θ": [t: "CGL⠹"] # 0x398 + - "Ι": [t: "CGL⠊"] # 0x399 + - "Κ": [t: "CGL⠅"] # 0x39a + - "Λ": [t: "CGL⠇"] # 0x39b + - "Μ": [t: "CGL⠍"] # 0x39c + - "Ν": [t: "CGL⠝"] # 0x39d + - "Ξ": [t: "CGL⠭"] # 0x39e + - "Ο": [t: "CGL⠕"] # 0x39f + - "Π": [t: "CGL⠏"] # 0x3a0 + - "Ρ": [t: "CGL⠗"] # 0x3a1 + - "΢": [t: "CGL⠎"] # 0x3a2 + - "Σ": [t: "CGL⠎"] # 0x3a3 + - "Τ": [t: "CGL⠞"] # 0x3a4 + - "Υ": [t: "CGL⠥"] # 0x3a5 + - "Φ": [t: "CGL⠋"] # 0x3a6 + - "Χ": [t: "CGL⠯"] # 0x3a7 + - "Ψ": [t: "CGL⠽"] # 0x3a8 + - "Ω": [t: "CGL⠺"] # 0x3a9 + + - "µ": [t: "GL⠍"] # 0x00B5 (Micro (Greek mu)) + - "Ω": [t: "CGL⠺"] # 0x2126 (Ohm sign (capital Greek omega)) + - "∆": [t: "CGL⠙"] # 0x2206 (Increment (capital Greek delta)) + - "∏": [t: "CGL⠏"] # 0x220F (Product (capital Greek pi)) + + - "⁡": [t: ""] # 0x2061⁡ (invisible function apply) + - "⁢": [t: ""] # 0x2062 (invisible times) + - "⁣": [t: ""] # 0x2063⁡ (invisible separator) + - "⁤": [t: ""] # 0x2064 (invisible plus) + + + - "!": [t: "⠸⠔"] # 0x0021 (factorial) + - "%": [t: "⠐⠬"] # 0x0025 (percent sign) + - "(": [t: "⠦"] # 0x0028 (left parenthesis) + - ")": [t: "⠴"] # 0x0029 (right parenthesis) + - "+": [t: "⠖"] # 0x002B (regular plus) + - ",": [t: "⠂"] # 0x002C (comma) + - "-": [t: "⠤"] # 0x002D (regular minus) + - ".": [t: "⠂"] # 0x002E (decimal point (American and Continental)) + - "/": [t: "⠌"] # 0x002F (diagonal fraction line) + - "0": [t: "N⠼"] # 0x0030 (digit zero) + - "1": [t: "N⠡"] # 0x0031 (digit one) + - "2": [t: "N⠣"] # 0x0032 (digit two) + - "3": [t: "N⠩"] # 0x0033 (digit three) + - "4": [t: "N⠹"] # 0x0034 (digit four) + - "5": [t: "N⠱"] # 0x0035 (digit five) + - "6": [t: "N⠫"] # 0x0036 (digit six) + - "7": [t: "N⠻"] # 0x0037 (digit seven) + - "8": [t: "N⠳"] # 0x0038 (digit eight) + - "9": [t: "N⠪"] # 0x0039 (digit nine) + - ":": [t: "⠒"] # 0x003A (colon) + - "<": [t: "⠐⠣"] # 0x003C (less than sign) + - "=": [t: "⠶"] # 0x003D (regular equals sign) + - ">": [t: "⠐⠜"] # 0x003E (greater than sign ( regular )) + - "[": [t: "⠷"] # 0x005B (left square bracket) + - "]": [t: "⠾"] # 0x005D (right square bracket) + - "{": # 0x007B ( left curly brace) + - test: + if: "following-sibling::*[1][self::m:mtable]" + then_test: + if: "count(following-sibling::*[1]/*) = 1" + then: [t: "⠨⠦"] + else: [t: "⠸⠦"] + else: [t: "⠨⠦"] # regular brace + - "|": [t: "⠿"] # 0x007C (vertical bar) + - "}": [t: "⠨⠴"] # 0x007D (right curly brace) + - "~": [t: "⠘⠶"] # 0x007E (simple tilde (is related to, is similar)) + - "¢": [t: "⠉"] # 0x00A2 (cent) + - "°": [t: "⠐⠕"] # 0x00B0 (degree sign) + - "±": [t: "⠖⠤"] # 0x00B1 (plus or minus) + - "¼": [t: "⠡⠌⠹"] # 0x00BC (vulgar fraction one quarter) + - "½": [t: "⠡⠌⠣"] # 0x00BD (vulgar fraction one half) + - "¾": [t: "⠩⠌⠹"] # 0x00BE (vulgar fraction three quarters) + - "×": [t: "⠔"] # 0x00D7 ( multiplication sign) + - "÷": [t: "⠒"] # 0x00F7 (division sign ( divided by )) + - "ב": [t: "⠘⠘⠃"] # 0x05D1 ( beth) + - "ג": [t: "⠘⠘⠛"] # 0x05D2 (gimel) + - "ד": [t: "⠘⠘⠙"] # 0x05D3 (daleth) + - "‖": [t: "⠘⠿"] # 0x2016 (double vertical bar) + - "‰": [t: "⠐⠬⠬"] # 0x2030 (per mille sign) + - "′": [t: "⠄"] # 0x2032 (prime) + - "″": [t: "⠄⠄"] # 0x2033 (double prime) + - "‴": [t: "⠄⠄⠄"] # 0x2034 (triple prime) + - "€": [t: "⠘⠑"] # 0x20AC (euro) + - "Å": [t: "⠨⠁⠈⠕"] # 0x212B (angstrom) + - "ℵ": [t: "⠘⠘⠁"] # 0x2135 (alef) + - "←": [t: "⠸⠪"] # 0x2190 (left pointing arrow) + - "↑": [t: "⠘⠻"] # 0x2191 (arrow pointing up) + - "→": [t: "⠸⠱"] # 0x2192 (left pointing arrow) + - "↓": [t: "⠨⠻"] # 0x2193 (arrow pointing down) + - "↔": [t: "⠐⠻"] # 0x2194 (horizontal two-way arrow) + - "↖": [t: "⠘⠪"] # 0x2196 (north west arrow) + - "↗": [t: "⠘⠱"] # 0x2197 (north east arrow) + - "↘": [t: "⠨⠱"] # 0x2198 (south east arrow) + - "↙": [t: "⠨⠪"] # 0x2199 (south west arrow) + - "↚": [t: "⠨⠸⠪"] # 0x219A (leftwards arrow with stroke) + - "↛": [t: "⠨⠸⠱"] # 0x219B (rightwards arrow with stroke) + - "↦": [t: "⠐⠱"] # 0x21A6 (rightwards arrow from bar (maps to)) + - "⇄": [t: "⠸⠻"] # 0x21C4 (arrow pointing right over arrow pointing left) + - "⇍": [t: "⠨⠐⠒"] # 0x21CD (leftwards double arrow with stroke) + - "⇎": [t: "⠨⠐⠒⠂"] # 0x21CE (left right double arrow with stroke (not equivalent)) + - "⇏": [t: "⠨⠒⠂"] # 0x21CF (rightwards double arrow with stroke (does not imply)) + - "⇐": [t: "⠐⠒"] # 0x21D0 (leftwards double arrow) + - "⇒": [t: "⠒⠂"] # 0x21D2 (rightwards double arrow (implication)) + - "⇔": [t: "⠐⠒⠂"] # 0x21D4 (left right double arrow (double implication, equivalence)) + - "∀": [t: "⠸⠌"] # 0x2200 (universal quantifier (for all, for each, for every)) + - "∁": [t: "⠨⠩"] # 0x2201 (capital english c (complement)) + - "∂": [t: "⠐⠙"] # 0x2202 (partial derivative (round d)) + - "∃": [t: "⠸⠡"] # 0x2203 (existential quantifier (there exists uniquely, for exactly one)) + - "∄": [t: "⠨⠸⠡"] # 0x2204 (does not exist) + - "∅": [t: "⠘⠼"] # 0x2205 (empty set ( represented by zero with vertical or oblique bar through it)) + - "∇": [t: "⠨⠹"] # 0x2207 (del (nabla, gradient) , inverted triangle) + - "∈": [t: "⠘⠡"] # 0x2208 (membership (is an element of)) + - "∉": [t: "⠘⠌"] # 0x2209 (is not an element of) + - "∋": [t: "⠨⠘⠡"] # 0x220B (reverse membership (contains the element)) + - "∌": [t: "⠨⠘⠌"] # 0x220C (does not contain as a member) + - "∑": [t: "⠨⠘⠎"] # 0x2211 (n-ary summation) + - "∓": [t: "⠤⠖"] # 0x2213 (minus or plus) + - "∘": [t: "⠸⠼"] # 0x2218 (hollow dot) + - "∞": [t: "⠘⠉"] # 0x221E (infinity) + - "∥": [t: "⠸⠳"] # 0x2225 (is parallel to) + - "∧": [t: "⠘⠔"] # 0x2227 (logical product (and, meet)) + - "∨": [t: "⠘⠢"] # 0x2228 (logical sum (join,or)) + - "∩": [t: "⠘⠖"] # 0x2229 (intersection sign ( cap )) + - "∪": [t: "⠸⠖"] # 0x222A (union sign) + - "∫": [t: "⠯"] # 0x222B (single integral) + - "∬": [t: "⠯⠯"] # 0x222C (double integral) + - "∮": [t: "⠨⠯"] # 0x222E (integral with superposed circle) + - "∯": [t: "⠨⠯⠯"] # 0x222F (double contour integral/surface integral) + - "≃": [t: "⠐⠶"] # 0x2243 (bar under simple tilde (asymptotically equal to)) + - "≅": [t: "⠸⠶"] # 0x2245 (equals sign under tilde) + - "≈": [t: "⠐⠶"] # 0x2248 (double tilde) + - "≙": [t: "⠒⠶"] # 0x2259 (caret over equals sign) + - "≠": [t: "⠨⠶"] # 0x2260 (not equal to) + - "≡": [t: "⠶⠶"] # 0x2261 (identify (is congruent to, is identical with)) + - "≢": [t: "⠨⠶⠶"] # 0x2262 (is not identical to) + - "≤": [t: "⠘⠣"] # 0x2264 (bar under less than sign (is less than or equal to)) + - "≥": [t: "⠘⠜"] # 0x2265 (bar under greater than sign (is greater than or equal to)) + - "≪": [t: "⠐⠐⠣"] # 0x226A (nest of two less than signs with straight sides (is small compared with)) + - "≫": [t: "⠐⠐⠜"] # 0x226B (nest of two greater than signs with straight sides (is large compared with)) + - "≺": [t: "⠨⠨⠣"] # 0x227A (less than sign with curved sides) + - "≻": [t: "⠨⠨⠜"] # 0x227B (greater than sign with curved sides ) + - "≼": [t: "⠘⠘⠣"] # 0x227C (less than sign with curved sides over curved bar (precedes or is equal to)) + - "≽": [t: "⠘⠘⠜"] # 0x227D (greater than sign with curved sides over curved bar (succeeds or is equal to)) + - "⊂": [t: "⠨⠡"] # 0x2282 (inclusion sign ( is contained in, is a subset of )) + - "⊃": [t: "⠐⠡"] # 0x2283 (reverse inclusion sign ( contains, implies )) + - "⊄": [t: "⠨⠌"] # 0x2284 (not a subset of) + - "⊅": [t: "⠐⠌"] # 0x2285 (not a superset of) + - "⊆": [t: "⠸⠨⠡"] # 0x2286 (bar under inclusion sign (is a subset of)) + - "⊈": [t: "⠸⠨⠌"] # 0x2288 (neither a subset of nor equal to) + - "⊕": [t: "⠨⠖"] # 0x2295 (circle with interior plus sign (exclusive or, direct sum, diletion)) + - "⊗": [t: "⠨⠔"] # 0x2297 (circle with interior cross (tensor product)) + - "⊥": [t: "⠘⠳"] # 0x22A5 (is perpendicular to) + - "⋅": [t: "⠔⠔"] # 0x22C5 (dot,and times) + - "⌄": [t: "⠸⠘⠒"] # 0x2304 (inverted caret) + - "⏜": [t: "⠈⠒"] # 0x23DC (arc concave upward) + - "△": [t: "⠨⠘⠙"] # 0x25B3 (increment (laplacian Delta) ) + - "✕": [t: "⠐⠐⠔"] # 0x2715 (multiplication X) + - "⟧": [t: "⠨⠾"] # 0x27E7 (right barred bracket) + - "⪥": [t: "⠐⠜⠐⠣"] # 0x2AA5 (greater than sign followed by less than sign) diff --git a/src/braille.rs b/src/braille.rs index 5399d0446..b368be66b 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -2,7 +2,7 @@ use strum_macros::Display; use sxd_document::dom::{Element, ChildOfElement}; use sxd_document::Package; -use crate::definitions::SPEECH_DEFINITIONS; +use crate::definitions::BRAILLE_DEFINITIONS; use crate::errors::*; use crate::pretty_print::mml_to_string; use crate::prefs::PreferenceManager; @@ -40,6 +40,7 @@ pub fn braille_mathml(mathml: Element, nav_node_id: &str) -> Result<(String, usi "Vietnam" => vietnam_cleanup(pref_manager, braille_string), "CMU" => cmu_cleanup(pref_manager, braille_string), "Finnish" => finnish_cleanup(pref_manager, braille_string), + "French" => french_cleanup(pref_manager, braille_string), "Swedish" => swedish_cleanup(pref_manager, braille_string), "LaTeX" => LaTeX_cleanup(pref_manager, braille_string), "ASCIIMath" => ASCIIMath_cleanup(pref_manager, braille_string), @@ -2046,6 +2047,97 @@ fn swedish_cleanup(pref_manager: Ref, raw_braille: String) -> return result.to_string(); } +static FRENCH_INDICATOR_REPLACEMENTS: phf::Map<&str, &str> = phf_map! { + // FIX: this needs to be fully checked against the French spec + // FIX: this needs cleaning up -- not all of these are used + "S" => "XXX", // sans-serif -- from prefs + "B" => "⠸", // bold + "𝔹" => "XXX", // blackboard -- from prefs + "T" => "⠈", // script + "I" => "⠸", // italic + "R" => "", // roman + "E" => "⠰", // English + "1" => "⠀", // Grade 1 symbol (used for number followed by a letter) + "L" => "", // Letter left in to assist in locating letters + "D" => "XXX", // German (Deutsche) -- from prefs + "G" => "⠘", // Greek + "V" => "XXX", // Greek Variants + // "H" => "⠠⠠", // Hebrew + // "U" => "⠈⠈", // Russian + "C" => "⠨", // capital + "𝑐" => "", // second or latter braille cell of a capital letter + "𝐶" => "⠨", // capital that never should get whitespace in front (from chemical element) + "N" => "", // number indicator + "n" => "", // number indicator for drop numbers (special case with close parens) + "t" => "⠱", // shape terminator + "W" => "⠀", // whitespace" + "𝐖"=> "⠀", // whitespace + "s" => "⠆", // typeface single char indicator + "w" => "", // typeface word indicator + "e" => "", // typeface & capital terminator + "," => "⠂", // comma + "." => "⠲", // period + "-" => "-", // hyphen + "—" => "⠠⠤", // normal dash (2014) -- assume all normal dashes are unified here [RUEB appendix 3] + "―" => "⠐⠠⠤", // long dash (2015) -- assume all long dashes are unified here [RUEB appendix 3] + "(" => "⠦", // Not really needed, but done for consistancy with ")" + ")" => "⠴", // Needed for rules with drop numbers to avoid mistaking for dropped 0 + "↑" => "⠬", // superscript + "↓" => "⠡", // subscript + "#" => "", // signals end of script + "Z" => "⠐", // signals end of index of root, integrand/lim from function ("zone change") + +}; + +fn french_cleanup(pref_manager: Ref, raw_braille: String) -> String { + // FIX: need to implement this -- this is just a copy of the Vietnam code + lazy_static! { + // Empty bases are ok if they follow whitespace + static ref EMPTY_BASE: Regex = Regex::new(r"(^|[W𝐖w])E").unwrap(); + } + debug!("french_cleanup: start={}", raw_braille); + // let result = typeface_to_word_mode(&raw_braille); + // let result = capitals_to_word_mode(&result); + + let result = raw_braille.replace("CG", "⠘") + .replace("𝔹C", "⠩") + .replace("DC", "⠰"); + + debug!(" after typeface/caps={}", &result); + + // these typeforms need to get pulled from user-prefs as they are transcriber-defined + let double_struck = pref_manager.pref_to_string("Vietnam_DoubleStruck"); + let sans_serif = pref_manager.pref_to_string("Vietnam_SansSerif"); + let fraktur = pref_manager.pref_to_string("Vietnam_Fraktur"); + let greek_variant = pref_manager.pref_to_string("Vietnam_GreekVariant"); + + // This reuses the code just for getting rid of unnecessary "L"s and "N"s + let result = remove_unneeded_mode_changes(&result, UEB_Mode::Grade1, UEB_Duration::Passage); + debug!(" after removing mode changes={}", &result); + + + let result = EMPTY_BASE.replace_all(&result, "$1"); + let result = REPLACE_INDICATORS.replace_all(&result, |cap: &Captures| { + let matched_char = &cap[0]; + match matched_char { + "𝔹" => &double_struck, + "S" => &sans_serif, + "D" => &fraktur, + "V" => &greek_variant, + _ => match FRENCH_INDICATOR_REPLACEMENTS.get(matched_char) { + None => {error!("REPLACE_INDICATORS and FRENCH_INDICATOR_REPLACEMENTS are not in sync: missing '{}'", matched_char); ""}, + Some(&ch) => ch, + }, + } + }); + + // Remove unicode blanks at start and end -- do this after the substitutions because ',' introduces spaces + // let result = result.trim_start_matches('⠀').trim_end_matches('⠀'); + let result = COLLAPSE_SPACES.replace_all(&result, "⠀"); + + return result.to_string(); +} + #[allow(non_snake_case)] fn LaTeX_cleanup(_pref_manager: Ref, raw_braille: String) -> String { lazy_static! { @@ -2213,6 +2305,7 @@ impl BrailleChars { "Vietnam" => BrailleChars:: get_braille_vietnam_chars(node, text_range), "Swedish" => BrailleChars:: get_braille_ueb_chars(node, text_range), // FIX: need to figure out what to implement "Finnish" => BrailleChars:: get_braille_ueb_chars(node, text_range), // FIX: need to figure out what to implement + "French" => BrailleChars:: get_braille_ueb_chars(node, text_range), // FIX: need to figure out what to implement _ => return Err(sxd_xpath::function::Error::Other(format!("get_braille_chars: unknown braille code '{}'", code))) }; return match result { @@ -2707,9 +2800,9 @@ impl NeedsToBeGrouped { // '¨', etc., brailles as two chars -- there probably is some exception list but I haven't found it -- these are the ones I know about return !((is_one_char && !['¨', '″', '‴', '⁗'].contains(&first_char)) || // clause 8 // "lim", "cos", etc., appear not to get parens, but the rules don't mention it (tests show it) - IsInDefinition::is_defined_in(text, &SPEECH_DEFINITIONS, "FunctionNames").unwrap() || - IsInDefinition::is_defined_in(text, &SPEECH_DEFINITIONS, "Arrows").unwrap() || // clause 4 - IsInDefinition::is_defined_in(text, &SPEECH_DEFINITIONS, "GeometryShapes").unwrap()); // clause 5 + IsInDefinition::is_defined_in(text, &BRAILLE_DEFINITIONS, "FunctionNames").unwrap() || + IsInDefinition::is_defined_in(text, &BRAILLE_DEFINITIONS, "Arrows").unwrap() || // clause 4 + IsInDefinition::is_defined_in(text, &BRAILLE_DEFINITIONS, "GeometryShapes").unwrap()); // clause 5 }, "mrow" => { // check for bracketed exprs @@ -2751,6 +2844,82 @@ impl NeedsToBeGrouped { } } + + /// FIX: what needs to be implemented? + fn needs_grouping_for_french(mathml: Element, is_base: bool) -> bool { + use crate::xpath_functions::IsInDefinition; + let mut node_name = name(&mathml); + if mathml.attribute_value("data-roman-numeral").is_some() { + node_name = "mi"; // roman numerals don't follow number rules + } + + // FIX: the leaf rules are from UEB -- check the French rules + match node_name { + "mn" => { + if !is_base { + return false; + } // clause 1 + // two 'mn's can be adjacent, in which case we need to group the 'mn' to make it clear it is separate (see bug #204) + let parent = get_parent(mathml); // there is always a "math" node + let grandparent = if name(&parent) == "math" {parent} else {get_parent(parent)}; + if name(&grandparent) != "mrow" { + return false; + } + let preceding = parent.preceding_siblings(); + if preceding.len() < 2 { + return false; + } + // any 'mn' would be separated from this node by invisible times + let previous_child = as_element(preceding[preceding.len()-1]); + if name(&previous_child) == "mo" && as_text(previous_child) == "\u{2062}" { + let previous_child = as_element(preceding[preceding.len()-2]); + return name(&previous_child) == "mn" + } else { + return false; + } + }, + "mo" => { + return !is_base; // if in script, then what follows is assumed to be an operand in braille, but it isn't, so add grouping + }, + "mi" | "mtext" => { + let text = as_text(mathml); + let parent = get_parent(mathml); // there is always a "math" node + let parent_name = name(&parent); + return text.chars().count() > 1 && + parent_name != "mrow" && + !IsInDefinition::is_defined_in(text, &BRAILLE_DEFINITIONS, "FunctionNames").unwrap(); + }, + "mrow" => { + // check for bracketed exprs + if IsBracketed::is_bracketed(mathml, "", "", false, true) { + return false; + } + + // check for prefix at start + // example 7.12 has "2-" in superscript and is grouped, so we don't consider postfix ops + let children = mathml.children(); + if children.len() == 2 && + (name( &as_element(children[0])) == "mo") { + return false; + } + + if children.len() == 3 { + let child0 = as_element(children[0]); + let operator = as_element(children[1]); // invisible function op? + // debug!("needs_grouping_for_french: child0 ="); + if name(&operator) == "mo" && as_text(operator) == "\u{2061}" && + (name(&child0) == "mi" || name(&child0) == "mtext") && + IsInDefinition::is_defined_in(as_text(child0), &BRAILLE_DEFINITIONS, "TrigFunctionNames").unwrap() { + return false; + } + } + return true; + }, + "msub" | "msup" | "msubsup" => return true, + _ => return false, + } + } + // ordinals often have an irregular start (e.g., "half") before becoming regular. // if the number is irregular, return the ordinal form, otherwise return 'None'. fn needs_grouping_for_swedish(mathml: Element, is_base: bool) -> bool { @@ -2775,9 +2944,9 @@ impl NeedsToBeGrouped { // '¨', etc., brailles as two chars -- there probably is some exception list but I haven't found it -- these are the ones I know about return !((is_one_char && !['¨', '″', '‴', '⁗'].contains(&first_char)) || // clause 8 // "lim", "cos", etc., appear not to get parens, but the rules don't mention it (tests show it) - IsInDefinition::is_defined_in(text, &SPEECH_DEFINITIONS, "FunctionNames").unwrap() || - IsInDefinition::is_defined_in(text, &SPEECH_DEFINITIONS, "Arrows").unwrap() || // clause 4 - IsInDefinition::is_defined_in(text, &SPEECH_DEFINITIONS, "GeometryShapes").unwrap()); // clause 5 + IsInDefinition::is_defined_in(text, &BRAILLE_DEFINITIONS, "FunctionNames").unwrap() || + IsInDefinition::is_defined_in(text, &BRAILLE_DEFINITIONS, "Arrows").unwrap() || // clause 4 + IsInDefinition::is_defined_in(text, &BRAILLE_DEFINITIONS, "GeometryShapes").unwrap()); // clause 5 }, "mrow" => { // check for bracketed exprs @@ -2863,9 +3032,9 @@ impl NeedsToBeGrouped { // '¨', etc., brailles as two chars -- there probably is some exception list but I haven't found it -- these are the ones I know about return !((is_one_char && !['¨', '″', '‴', '⁗'].contains(&first_char)) || // clause 8 // "lim", "cos", etc., appear not to get parens, but the rules don't mention it (tests show it) - IsInDefinition::is_defined_in(text, &SPEECH_DEFINITIONS, "FunctionNames").unwrap() || - IsInDefinition::is_defined_in(text, &SPEECH_DEFINITIONS, "Arrows").unwrap() || // clause 4 - IsInDefinition::is_defined_in(text, &SPEECH_DEFINITIONS, "GeometryShapes").unwrap()); // clause 5 + IsInDefinition::is_defined_in(text, &BRAILLE_DEFINITIONS, "FunctionNames").unwrap() || + IsInDefinition::is_defined_in(text, &BRAILLE_DEFINITIONS, "Arrows").unwrap() || // clause 4 + IsInDefinition::is_defined_in(text, &BRAILLE_DEFINITIONS, "GeometryShapes").unwrap()); // clause 5 }, "mfrac" => return false, // clause 2 (test GTM 8.2(4) shows numeric fractions are not special) "msqrt" | "mroot" => return false, // clause 3 @@ -2906,6 +3075,7 @@ impl Function for NeedsToBeGrouped { "CMU" => NeedsToBeGrouped::needs_grouping_for_cmu(e, is_base), "UEB" => NeedsToBeGrouped::needs_grouping_for_ueb(e, is_base), "Finnish" => NeedsToBeGrouped::needs_grouping_for_finnish(e, is_base), + "French" => NeedsToBeGrouped::needs_grouping_for_french(e, is_base), "Swedish" => NeedsToBeGrouped::needs_grouping_for_swedish(e, is_base), _ => return Err(XPathError::Other(format!("NeedsToBeGrouped: braille code arg '{:?}' is not a known code ('UEB', 'CMU', or 'Swedish')", braille_code))), }; diff --git a/tests/braille/French/chemistry.rs b/tests/braille/French/chemistry.rs new file mode 100644 index 000000000..4af951eda --- /dev/null +++ b/tests/braille/French/chemistry.rs @@ -0,0 +1,47 @@ +// French braille tests for the basic mathml tags +// These tests are from the French braille authority's publication "NOTATION PIATHÉMATIQUE BRAILLE" (Première édition janvier 2007) +// https://www.avh.asso.fr/sites/default/files/notation_mathematique_braille2_0.pdf +use crate::common::*; + +#[test] +fn bond_I_4_1() { + let expr= r#"H-H"#; + test_braille("French", expr, "⠨⠓⠤⠨⠓"); +} + +#[test] +fn bond_I_4_2() { + let expr= r#"O=O"#; + test_braille("French", expr, "⠨⠕⠶⠨⠕"); +} + +#[test] +fn ion_II_1_3() { + let expr= r#"SO42-"#; + test_braille("French", expr, "⠨⠎⠨⠕⠢⠹⠈⠰⠣⠤⠆"); +} + +#[test] +fn equilibrium_III_5_1() { + let expr= r#"N2(g)+3H2(g)⇌2NH3(g)"#; + test_braille("French", expr, "⠨⠝⠣⠦⠛⠴⠖⠩⠨⠓⠣⠦⠛⠴⠸⠻⠣⠨⠝⠨⠓⠩⠦⠛⠴"); +} + +#[test] +fn equilibrium_III_5_2() { + let expr= r#"2H2O + ⇌(2)(1)H3O++OH-"#; + test_braille("French", expr, " ⠣⠨⠓⠣⠨⠕⠰⠸⠻⠆⠢⠢⠦⠣⠴⠈⠈⠦⠡⠴⠨⠓⠩⠨⠕⠈⠰⠖⠆⠖⠨⠕⠨⠓⠈⠰⠤⠆"); +} + +#[test] +fn equilibrium_III_5_3() { + let expr= r#"R-CO-O- + H+R'-OH + ⇌hydrolyseestérification + R-CO-O- + R'+ + H2O"#; + test_braille("French", expr, " ⠨⠗⠤⠨⠉⠨⠕⠤⠨⠕⠤⠨⠓⠖⠨⠗⠄⠤⠨⠕⠨⠓ ⠸⠻⠨⠢⠰⠓⠽⠙⠗⠕⠇⠽⠎⠑⠆⠨⠔⠈⠰⠑⠎⠞⠗⠊⠋⠊⠉⠁⠞⠊⠕⠝⠆⠨⠗⠤⠨⠉⠨⠕⠤⠨⠕⠤⠨⠗⠄⠖⠨⠓⠢⠣⠨⠕"); +} diff --git a/tests/braille/French/french.rs b/tests/braille/French/french.rs new file mode 100644 index 000000000..273c28f62 --- /dev/null +++ b/tests/braille/French/french.rs @@ -0,0 +1,138 @@ +// French braille tests for the basic mathml tags +// These tests are from the French braille authority's publication "NOTATION PIATHÉMATIQUE BRAILLE" (Première édition janvier 2007) +// https://www.avh.asso.fr/sites/default/files/notation_mathematique_braille2_0.pdf +use crate::common::*; + +#[test] +fn fraction_7_1() { + let expr= r#"23=46"#; + test_braille("French", expr, "⠣⠌⠩⠶⠹⠌⠫"); +} + +#[test] +fn fraction_8_1() { + let expr= r#"a+ba"#; + test_braille("French", expr, "⠰⠁⠖⠃⠆⠌⠁"); +} + +#[test] +fn superscript_8_4() { + let expr= r#"e7"#; + test_braille("French", expr, "⠑⠈⠻"); +} + +#[test] +fn superscript_8_6() { + let expr= r#"e-π"#; + test_braille("French", expr, "⠑⠈⠤⠘⠏"); +} + +#[test] +fn subscript_9_2_1() { + let expr= r#"ap"#; + test_braille("French", expr, "⠁⠢⠏"); +} + +#[test] +fn subscript_9_2_3() { + let expr= r#"un+1"#; + test_braille("French", expr, "⠥⠢⠰⠝⠖⠡⠆"); +} + +#[test] +fn scripts_9_4_1() { + let expr= r#"a0'"#; + test_braille("French", expr, "⠁⠄⠢⠼"); +} + +#[test] +fn scripts_9_4_3() { + let expr= r#"Cmp= + Cmm-p"#; + test_braille("French", expr, "⠨⠉⠢⠍⠈⠏⠶⠨⠉⠢⠍⠈⠰⠍⠤⠏⠆"); +} + +#[test] +fn scripts_9_5_1() { + let expr= r#"At"#; + test_braille("French", expr, "⠈⠞⠨⠁"); +} + +#[test] +fn chem_9_5_4() { + let expr= r#"O816"#; + test_braille("French", expr, "⠨⠕⠠⠢⠳⠠⠈⠡⠫"); +} + +#[test] +fn root_10_3() { + let expr= r#"(a+b)36= + a+b"#; + test_braille("French", expr, "⠈⠫⠜⠰⠦⠁⠖⠃⠴⠈⠩⠆⠶⠜⠰⠁⠖⠃⠆"); +} + +#[test] +fn accent_12_1() { + let expr= r#"v→"#; + test_braille("French", expr, "⠨⠒⠧"); +} + +#[test] +fn accent_12_2() { + let expr= r#"AB¯"#; + test_braille("French", expr, "⠸⠒⠨⠁⠨⠃"); +} + +#[test] +fn accent_12_3() { + let expr= r#"AB⌒"#; + test_braille("French", expr, "⠈⠒⠨⠁⠨⠃"); +} + +#[test] +fn accent_12_5() { + let expr= r#"(Ox,Oy)^"#; + test_braille("French", expr, "⠘⠒⠦⠨⠕⠭⠂⠨⠕⠽⠴"); +} + +#[test] +fn equations_13_4_1() { + init_logger(); + let expr= r#" + 2x+3y=5 + 3x-7y=8 + "#; + test_braille("French", expr, "⠸⠦⠣⠭⠖⠩⠽⠶⠱⠀⠠⠜⠀⠩⠭⠤⠻⠽⠶⠳"); +} + +#[test] +fn lim_14_3() { + let expr= r#"limx→+∞ + ln(x+2)x=0"#; + test_braille("French", expr, " ⠇⠊⠍⠢⠢⠰⠭⠸⠱⠖⠘⠉⠆⠇⠝⠦⠭⠖⠣⠴⠌⠭⠶⠼"); +} + +#[test] +fn integral_14_4_3() { + let expr= r#"∯Sf(x,y)dxdy"#; + test_braille("French", expr, "⠨⠯⠯⠢⠨⠎⠋⠦⠭⠂⠽⠴⠙⠭⠙⠽"); +} + +#[test] +fn log_14_5_1() { + let expr= r#"lnab"#; + test_braille("French", expr, "⠇⠝⠦⠁⠌⠃⠴"); +} + +#[test] +fn log_14_5_3() { + let expr= r#"log7x= + lnxln7"#; + test_braille("French", expr, "⠇⠕⠛⠢⠻⠰⠭⠆⠶⠇⠝⠰⠭⠆⠌⠇⠝⠰⠻⠆"); +} + +#[test] +fn symbols_15_1() { + let expr= r#"∃x:x∈A"#; + test_braille("French", expr, "⠸⠡⠭⠒⠭⠘⠡⠨⠁"); +} From 7b7570941dd1138648e413c325db48587814501a Mon Sep 17 00:00:00 2001 From: NSoiffer Date: Fri, 27 Sep 2024 15:30:35 -0700 Subject: [PATCH 002/120] Added a table of atomic numbers for the elements and added a test for atomic numbers in prescripts to bump up their liklihood number. This allows single character elements with pre subscripts that match their atomic number to be considered chemistry. --- src/chemistry.rs | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/src/chemistry.rs b/src/chemistry.rs index 10c99af9b..17244e04e 100644 --- a/src/chemistry.rs +++ b/src/chemistry.rs @@ -376,7 +376,7 @@ pub fn convert_leaves_to_chem_elements(mathml: Element) -> Option> return None; // can't be an chemical letter } let chem_element = unsafe{ str::from_utf8_unchecked(&bytes_str[..n]) }; - if CHEMICAL_ELEMENTS.contains_key( chem_element ) { + if CHEMICAL_ELEMENT_ELECTRONEGATIVITIES.contains_key( chem_element ) { return Some( new_chemical_element(doc, chem_element) ); } @@ -1109,7 +1109,7 @@ fn likely_chem_formula(mathml: Element) -> isize { fn is_order_ok(mrow: Element) -> bool { assert_eq!(name(&mrow), "mrow"); if let Some(elements) = collect_elements(mrow) { - if elements.iter().any(|&e| !CHEMICAL_ELEMENTS.contains_key(e)) { + if elements.iter().any(|&e| !CHEMICAL_ELEMENT_ELECTRONEGATIVITIES.contains_key(e)) { return false; } let n_elements = elements.len(); @@ -1207,7 +1207,7 @@ fn is_ordered_by_electronegativity(elements: &[&str]) -> bool { // HPO_4^2 (Monohydrogen phosphate) doesn't fit this pattern, nor does HCO_3^- (Hydrogen carbonate) and some others // FIX: drop "H" from the ordering?? assert!(!elements.len() > 1); // already handled - return elements.windows(2).all(|pair| CHEMICAL_ELEMENTS.get(pair[0]).unwrap() < CHEMICAL_ELEMENTS.get(pair[1]).unwrap()); + return elements.windows(2).all(|pair| CHEMICAL_ELEMENT_ELECTRONEGATIVITIES.get(pair[0]).unwrap() < CHEMICAL_ELEMENT_ELECTRONEGATIVITIES.get(pair[1]).unwrap()); } fn is_generalized_salt(elements: &[&str]) -> bool { @@ -1300,7 +1300,16 @@ pub fn likely_adorned_chem_formula(mathml: Element) -> isize { // fix could make sure they are integers likelihood += 1; // looking like an atomic number if pre_subscript_name == "mn" { - likelihood += 1; // looking even more like an atomic number + // make sure the atomic number matches the base + let base = as_element(children[0]); + let base_name = name(&base); + if base_name == "mi" || base_name == "mtext" { + if let Some(atomic_number) = CHEMICAL_ELEMENT_ATOMIC_NUMBER.get(as_text(base)) { + if as_text(pre_subscript) == &atomic_number.to_string() { + likelihood = CHEMISTRY_THRESHOLD; + } + } + } } } else { return NOT_CHEMISTRY; @@ -1696,7 +1705,7 @@ fn convert_to_short_form(mathml: Element) -> Result { /// That list uses a horizontal line for the Lanthanide and Actinide Series. /// Because I had already ordered the elements before realizing that, I opened a gap and started the higher ones again with a '1' in front. /// The list is missing recent (unstable) elements -- I added them with the same value as the element above them in the periodic table. -static CHEMICAL_ELEMENTS: phf::Map<&str, u32> = phf_map! { +static CHEMICAL_ELEMENT_ELECTRONEGATIVITIES: phf::Map<&str, u32> = phf_map! { "Ac" => 40, "Ag" => 155, "Al" => 163, "Am" => 29, "Ar" => 4, "As" => 172, "At" => 181, "Au" => 154, "B" => 164, "Ba" => 14, "Be" => 18, "Bh" => 137, "Bi" => 170, "Bk" => 27, "Br" => 183, "C" => 169, "Ca" => 16, "Cd" => 158, "Ce" => 56, "Cf" => 26, "Cl" => 184, "Cm" => 28, "Cn" => 157, "Co" => 148, "Cr" => 136, "Cs" => 8, "Cu" => 156, @@ -1716,6 +1725,23 @@ static CHEMICAL_ELEMENTS: phf::Map<&str, u32> = phf_map! { "Me" => 0, "Et" => 0, "R" => 0, /* "Ac" => 0, */ "Ph" => 0, "X" => 0, /* treated as an unknown */ }; + +// A map of the chemical elements and their atomic numbers +static CHEMICAL_ELEMENT_ATOMIC_NUMBER: phf::Map<&str, u32> = phf_map! { + "H" => 1, "He" => 2, "Li" => 3, "Be" => 4, "B" => 5, "C" => 6, "N" => 7, "O" => 8, "F" => 9, "Ne" => 10, + "Na" => 11, "Mg" => 12, "Al" => 13, "Si" => 14, "P" => 15, "S" => 16, "Cl" => 17, "Ar" => 18, "K" => 19, "Ca" => 20, + "Sc" => 21, "Ti" => 22, "V" => 23, "Cr" => 24, "Mn" => 25, "Fe" => 26, "Co" => 27, "Ni" => 28, "Cu" => 29, "Zn" => 30, + "Ga" => 31, "Ge" => 32, "As" => 33, "Se" => 34, "Br" => 35, "Kr" => 36, "Rb" => 37, "Sr" => 38, "Y" => 39, "Zr" => 40, + "Nb" => 41, "Mo" => 42, "Tc" => 43, "Ru" => 44, "Rh" => 45, "Pd" => 46, "Ag" => 47, "Cd" => 48, "In" => 49, "Sn" => 50, + "Sb" => 51, "Te" => 52, "I" => 53, "Xe" => 54, "Cs" => 55, "Ba" => 56, "La" => 57, "Ce" => 58, "Pr" => 59, "Nd" => 60, + "Pm" => 61, "Sm" => 62, "Eu" => 63, "Gd" => 64, "Tb" => 65, "Dy" => 66, "Ho" => 67, "Er" => 68, "Tm" => 69, "Yb" => 70, + "Lu" => 71, "Hf" => 72, "Ta" => 73, "W" => 74, "Re" => 75, "Os" => 76, "Ir" => 77, "Pt" => 78, "Au" => 79, "Hg" => 80, + "Tl" => 81, "Pb" => 82, "Bi" => 83, "Po" => 84, "At" => 85, "Rn" => 86, "Fr" => 87, "Ra" => 88, "Ac" => 89, "Th" => 90, + "Pa" => 91, "U" => 92, "Np" => 93, "Pu" => 94, "Am" => 95, "Cm" => 96, "Bk" => 97, "Cf" => 98, "Es" => 99, "Fm" => 100, + "Md" => 101, "No" => 102, "Lr" => 103, "Rf" => 104, "Db" => 105, "Sg" => 106, "Bh" => 107, "Hs" => 108, "Mt" => 109, "Ds" => 110, + "Rg" => 111, "Cn" => 112, "Nh" => 113, "Fl" => 114, "Mc" => 115, "Lv" => 116, "Ts" => 117, "Og" => 118, +}; + pub fn is_chemical_element(node: Element) -> bool { // FIX: allow name to be in an mrow (e.g., Na let name = name(&node); @@ -1724,7 +1750,7 @@ pub fn is_chemical_element(node: Element) -> bool { } let text = as_text(node); - return CHEMICAL_ELEMENTS.contains_key(text); + return CHEMICAL_ELEMENT_ELECTRONEGATIVITIES.contains_key(text); } From 5136cb34d86715d6f2bf2f4611f94a7eb28a8506 Mon Sep 17 00:00:00 2001 From: NSoiffer Date: Fri, 27 Sep 2024 15:47:02 -0700 Subject: [PATCH 003/120] renamed test --- tests/braille/French/french.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/braille/French/french.rs b/tests/braille/French/french.rs index 273c28f62..b2f873952 100644 --- a/tests/braille/French/french.rs +++ b/tests/braille/French/french.rs @@ -106,7 +106,7 @@ fn equations_13_4_1() { } #[test] -fn lim_14_3() { +fn lim_14_2_3() { let expr= r#"limx→+∞ ln(x+2)x=0"#; test_braille("French", expr, " ⠇⠊⠍⠢⠢⠰⠭⠸⠱⠖⠘⠉⠆⠇⠝⠦⠭⠖⠣⠴⠌⠭⠶⠼"); From 4ccda5f59813e110ef799b3440760567c185e2ec Mon Sep 17 00:00:00 2001 From: NSoiffer Date: Thu, 29 May 2025 21:36:04 -0700 Subject: [PATCH 004/120] Update braille.rs to compile after merging. Add French to the braille testing file. New French braille tests. --- src/braille.rs | 18 +- tests/braille.rs | 4 + tests/braille/French/french.rs | 443 +++++++++++++++++++++++++++++---- 3 files changed, 405 insertions(+), 60 deletions(-) diff --git a/src/braille.rs b/src/braille.rs index dddc77f6f..3f3dca9b1 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -2933,7 +2933,7 @@ impl NeedsToBeGrouped { /// FIX: what needs to be implemented? fn needs_grouping_for_french(mathml: Element, is_base: bool) -> bool { use crate::xpath_functions::IsInDefinition; - let mut node_name = name(&mathml); + let mut node_name = name(mathml); if mathml.attribute_value("data-roman-numeral").is_some() { node_name = "mi"; // roman numerals don't follow number rules } @@ -2946,8 +2946,8 @@ impl NeedsToBeGrouped { } // clause 1 // two 'mn's can be adjacent, in which case we need to group the 'mn' to make it clear it is separate (see bug #204) let parent = get_parent(mathml); // there is always a "math" node - let grandparent = if name(&parent) == "math" {parent} else {get_parent(parent)}; - if name(&grandparent) != "mrow" { + let grandparent = if name(parent) == "math" {parent} else {get_parent(parent)}; + if name(grandparent) != "mrow" { return false; } let preceding = parent.preceding_siblings(); @@ -2956,9 +2956,9 @@ impl NeedsToBeGrouped { } // any 'mn' would be separated from this node by invisible times let previous_child = as_element(preceding[preceding.len()-1]); - if name(&previous_child) == "mo" && as_text(previous_child) == "\u{2062}" { + if name(previous_child) == "mo" && as_text(previous_child) == "\u{2062}" { let previous_child = as_element(preceding[preceding.len()-2]); - return name(&previous_child) == "mn" + return name(previous_child) == "mn" } else { return false; } @@ -2969,7 +2969,7 @@ impl NeedsToBeGrouped { "mi" | "mtext" => { let text = as_text(mathml); let parent = get_parent(mathml); // there is always a "math" node - let parent_name = name(&parent); + let parent_name = name(parent); return text.chars().count() > 1 && parent_name != "mrow" && !IsInDefinition::is_defined_in(text, &BRAILLE_DEFINITIONS, "FunctionNames").unwrap(); @@ -2984,7 +2984,7 @@ impl NeedsToBeGrouped { // example 7.12 has "2-" in superscript and is grouped, so we don't consider postfix ops let children = mathml.children(); if children.len() == 2 && - (name( &as_element(children[0])) == "mo") { + (name( as_element(children[0])) == "mo") { return false; } @@ -2992,8 +2992,8 @@ impl NeedsToBeGrouped { let child0 = as_element(children[0]); let operator = as_element(children[1]); // invisible function op? // debug!("needs_grouping_for_french: child0 ="); - if name(&operator) == "mo" && as_text(operator) == "\u{2061}" && - (name(&child0) == "mi" || name(&child0) == "mtext") && + if name(operator) == "mo" && as_text(operator) == "\u{2061}" && + (name(child0) == "mi" || name(child0) == "mtext") && IsInDefinition::is_defined_in(as_text(child0), &BRAILLE_DEFINITIONS, "TrigFunctionNames").unwrap() { return false; } diff --git a/tests/braille.rs b/tests/braille.rs index d2406f23b..50331fdbe 100644 --- a/tests/braille.rs +++ b/tests/braille.rs @@ -33,5 +33,9 @@ mod braille { mod augenbit; mod other; } + + mod French { + mod french; + } } diff --git a/tests/braille/French/french.rs b/tests/braille/French/french.rs index b2f873952..46756ae44 100644 --- a/tests/braille/French/french.rs +++ b/tests/braille/French/french.rs @@ -4,135 +4,476 @@ use crate::common::*; #[test] -fn fraction_7_1() { - let expr= r#"23=46"#; +fn test_6_4_01() { + let expr= r#"E={a,b,c,d}"#; + test_braille("French", expr, "⠨⠑⠶⠨⠦⠁⠂⠃⠂⠉⠂⠙⠨⠴"); +} + +#[test] +fn test_6_4_02() { + let expr= r#"ℝ*=]−∞,0[∪]0,+∞["#; + test_braille("French", expr, "⠨⠨⠗⠈⠐⠔⠶⠾⠤⠘⠉⠂⠼⠷⠸⠖⠾⠼⠂⠖⠘⠉⠷"); +} + +#[test] +fn test_6_4_03() { + let expr= r#"x∈[0,1;2,3]"#; + test_braille("French", expr, "⠭⠘⠡⠷⠼⠂⠡⠆⠣⠂⠩⠾"); +} + +#[test] +fn test_6_4_04() { + let expr= r#"x∉ℝ+"#; + test_braille("French", expr, "⠭⠘⠌⠨⠨⠗⠈⠖"); +} + +#[test] +fn test_6_4_05() { + let expr= r#"D⊂F"#; + test_braille("French", expr, "⠨⠙⠨⠡⠨⠋"); +} + +#[test] +fn test_6_4_06() { + let expr= r#"D⊄E"#; + test_braille("French", expr, "⠨⠙⠨⠌⠨⠑"); +} + +#[test] +fn test_6_4_07() { + let expr= r#"A⊆B"#; + test_braille("French", expr, "⠨⠁⠸⠨⠡⠨⠃"); +} + +#[test] +fn test_6_4_08() { + let expr= r#"A∩B=⌀"#; + test_braille("French", expr, "⠨⠁⠘⠖⠨⠃⠶⠘⠼"); +} + +#[test] +fn test_6_4_09() { + let expr= r#"∁EF"#; + test_braille("French", expr, "⠨⠩⠢⠨⠑⠨⠋"); +} + +#[test] +fn test_7_01() { + let expr= r#"23=46"#; test_braille("French", expr, "⠣⠌⠩⠶⠹⠌⠫"); } #[test] -fn fraction_8_1() { +fn test_7_02() { + let expr= r#"1÷2=0,5"#; + test_braille("French", expr, "⠡⠒⠣⠶⠼⠂⠱"); +} + +#[test] +fn test_8_01() { let expr= r#"a+ba"#; - test_braille("French", expr, "⠰⠁⠖⠃⠆⠌⠁"); + test_braille("French", expr, "⠠⠄⠰⠁⠖⠃⠆⠌⠁"); +} + +#[test] +fn test_8_02() { + let expr= r#"a+bc+d"#; + test_braille("French", expr, "⠠⠄⠰⠁⠖⠃⠆⠌⠰⠉⠖⠙⠆"); } #[test] -fn superscript_8_4() { +fn test_8_03() { + let expr= r#"ab+cd=ad+bcbd"#; + test_braille("French", expr, "⠠⠄⠁⠌⠃⠖⠉⠌⠙⠶⠰⠁⠙⠖⠃⠉⠆⠌⠃⠙"); +} + +#[test] +fn test_8_04() { + let expr= r#"CardE=5"#; + test_braille("French", expr, "⠠⠄⠨⠉⠁⠗⠙⠰⠨⠑⠆⠶⠱"); +} + +#[test] +fn test_8_05() { + let expr= r#"ex+3"#; + test_braille("French", expr, "⠠⠄⠑⠈⠰⠭⠖⠩⠆"); +} + +#[test] +fn test_8_06() { let expr= r#"e7"#; - test_braille("French", expr, "⠑⠈⠻"); + test_braille("French", expr, "⠠⠑⠈⠻"); +} + +#[test] +fn test_8_07() { + let expr= r#"e−x"#; + test_braille("French", expr, "⠠⠑⠈⠤⠭"); +} + +#[test] +fn test_8_08() { + let expr= r#"e−π"#; + test_braille("French", expr, "⠠⠑⠈⠤⠘⠏"); +} + +#[test] +fn test_8_09() { + let expr= r#"x−1=1x"#; + test_braille("French", expr, "⠠⠭⠈⠤⠡⠶⠡⠌⠭"); +} + +#[test] +fn test_9_1_01() { + let expr= r#"a′"#; + test_braille("French", expr, "⠁⠄"); +} + +#[test] +fn test_9_1_02() { + let expr= r#"x″"#; + test_braille("French", expr, "⠭⠄⠄"); } #[test] -fn superscript_8_6() { - let expr= r#"e-π"#; - test_braille("French", expr, "⠑⠈⠤⠘⠏"); +fn test_9_1_03() { + let expr= r#"a′x+a″y+a‴z"#; + test_braille("French", expr, "⠁⠄⠭⠖⠁⠄⠄⠽⠖⠁⠄⠄⠄⠵"); } #[test] -fn subscript_9_2_1() { +fn test_9_2_01() { let expr= r#"ap"#; test_braille("French", expr, "⠁⠢⠏"); } #[test] -fn subscript_9_2_3() { +fn test_9_2_02() { + let expr= r#"x0"#; + test_braille("French", expr, "⠭⠢⠼"); +} + +#[test] +fn test_9_2_03() { let expr= r#"un+1"#; test_braille("French", expr, "⠥⠢⠰⠝⠖⠡⠆"); } #[test] -fn scripts_9_4_1() { - let expr= r#"a0'"#; +fn test_9_3_01() { + let expr= r#"x2"#; + test_braille("French", expr, "⠭⠈⠣"); +} + +#[test] +fn test_9_3_02() { + let expr= r#"xp+q"#; + test_braille("French", expr, "⠭⠈⠰⠏⠖⠟⠆"); +} + +#[test] +fn test_9_3_03() { + let expr= r#"a−5,3"#; + test_braille("French", expr, "⠁⠈⠤⠱⠂⠩"); +} + +#[test] +fn test_9_3_04() { + let expr= r#"aπ"#; + test_braille("French", expr, "⠁⠈⠘⠏"); +} + +#[test] +fn test_9_3_05() { + let expr= r#"523≠523"#; + test_braille("French", expr, "⠱⠈⠣⠜⠩⠨⠶⠱⠈⠰⠣⠜⠩⠆"); +} + +#[test] +fn test_9_3_06() { + let expr= r#"e−5x≠e−5x"#; + test_braille("French", expr, "⠑⠈⠰⠤⠱⠭⠆⠨⠶⠰⠑⠈⠤⠱⠆⠭"); +} + +#[test] +fn test_9_4_01() { + let expr= r#"a′0"#; test_braille("French", expr, "⠁⠄⠢⠼"); } #[test] -fn scripts_9_4_3() { - let expr= r#"Cmp= - Cmm-p"#; +fn test_9_4_02() { + let expr= r#"x0′2"#; + test_braille("French", expr, "⠭⠄⠢⠼⠈⠣"); +} + +#[test] +fn test_9_4_03() { + let expr= r#"∁mp=∁mm−p"#; test_braille("French", expr, "⠨⠉⠢⠍⠈⠏⠶⠨⠉⠢⠍⠈⠰⠍⠤⠏⠆"); } #[test] -fn scripts_9_5_1() { - let expr= r#"At"#; +fn test_9_4_04() { + let expr= r#"am+n′p+q"#; + test_braille("French", expr, "⠁⠄⠄⠢⠰⠍⠖⠝⠆⠈⠰⠏⠖⠟⠆"); +} + +#[test] +fn test_9_4_05() { + let expr= r#"ℝ+*"#; + test_braille("French", expr, "⠨⠨⠗⠢⠖⠈⠐⠔"); +} + +#[test] +fn test_9_5_01() { + let expr= r#"tA"#; test_braille("French", expr, "⠈⠞⠨⠁"); } #[test] -fn chem_9_5_4() { - let expr= r#"O816"#; +fn test_9_5_02() { + let expr= r#"2w"#; + test_braille("French", expr, "⠢⠣⠺"); +} + +#[test] +fn test_9_5_03() { + let expr= r#"tAtB"#; + test_braille("French", expr, "⠰⠈⠞⠨⠁⠆⠰⠈⠞⠨⠃⠆"); +} + +#[test] +fn test_9_5_04() { + let expr= r#"816O"#; test_braille("French", expr, "⠨⠕⠠⠢⠳⠠⠈⠡⠫"); } #[test] -fn root_10_3() { - let expr= r#"(a+b)36= - a+b"#; +fn test_10_01() { + let expr= r#"2"#; + test_braille("French", expr, "⠜⠣"); +} + +#[test] +fn test_10_02() { + let expr= r#"a4"#; + test_braille("French", expr, "⠈⠹⠜⠁"); +} + +#[test] +fn test_10_03() { + let expr= r#"(a+b)36=a+b"#; test_braille("French", expr, "⠈⠫⠜⠰⠦⠁⠖⠃⠴⠈⠩⠆⠶⠜⠰⠁⠖⠃⠆"); } #[test] -fn accent_12_1() { - let expr= r#"v→"#; +fn test_10_04() { + let expr= r#"a+a+b3"#; + test_braille("French", expr, "⠈⠩⠜⠰⠁⠖⠜⠰⠁⠖⠃⠆⠆"); +} + +#[test] +fn test_10_05() { + let expr= r#"a4b3"#; + test_braille("French", expr, "⠰⠈⠹⠜⠁⠆⠰⠈⠩⠜⠃⠆"); +} + +#[test] +fn test_11_01() { + let expr= r#"f:ℝ→ℝ,x↦y=1x"#; + test_braille("French", expr, "⠒⠨⠨⠗⠸⠱⠨⠨⠗⠭⠐⠱⠽⠶⠡⠌⠭"); +} + +#[test] +fn test_12_1_01() { + let expr= r#"v→"#; test_braille("French", expr, "⠨⠒⠧"); } #[test] -fn accent_12_2() { - let expr= r#"AB¯"#; +fn test_12_1_02() { + let expr= r#"AB¯"#; test_braille("French", expr, "⠸⠒⠨⠁⠨⠃"); } #[test] -fn accent_12_3() { - let expr= r#"AB⌒"#; +fn test_12_1_03() { + let expr= r#"AB⏜"#; test_braille("French", expr, "⠈⠒⠨⠁⠨⠃"); } #[test] -fn accent_12_5() { - let expr= r#"(Ox,Oy)^"#; +fn test_12_1_04() { + let expr= r#"AOB̂"#; + test_braille("French", expr, "⠘⠒⠨⠁⠨⠕⠨⠃"); +} + +#[test] +fn test_12_1_05() { + let expr= r#"(Ox,Oy)̂"#; test_braille("French", expr, "⠘⠒⠦⠨⠕⠭⠂⠨⠕⠽⠴"); } #[test] -fn equations_13_4_1() { - init_logger(); - let expr= r#" - 2x+3y=5 - 3x-7y=8 - "#; - test_braille("French", expr, "⠸⠦⠣⠭⠖⠩⠽⠶⠱⠀⠠⠜⠀⠩⠭⠤⠻⠽⠶⠳"); +fn test_12_1_06() { + let expr= r#"(OA→,OM→)̂"#; + test_braille("French", expr, "⠘⠒⠦⠨⠒⠨⠕⠨⠁⠂⠨⠒⠨⠕⠨⠍⠴"); +} + +#[test] +fn test_12_3_01() { + let expr= r#"(D)⫽(Δ)"#; + test_braille("French", expr, "⠦⠨⠙⠴⠸⠳⠦⠨⠘⠙⠴"); +} + +#[test] +fn test_12_3_02() { + let expr= r#"(AB)⊥(A′B′)"#; + test_braille("French", expr, "⠦⠨⠁⠨⠃⠴⠘⠳⠦⠨⠁⠄⠨⠃⠄⠴"); +} + +#[test] +fn test_13_2_01() { + let expr= r#"|x0|"#; + test_braille("French", expr, "⠿⠭⠢⠼⠿"); +} + +#[test] +fn test_13_2_02() { + let expr= r#"|a+ib|"#; + test_braille("French", expr, "⠿⠁⠖⠊⠃⠿"); +} + +#[test] +fn test_13_2_03() { + let expr= r#"∥ku→∥=|k|∥u→∥"#; + test_braille("French", expr, "⠘⠿⠅⠨⠒⠥⠘⠿⠶⠿⠅⠿⠘⠿⠨⠒⠥⠘⠿"); +} + +#[test] +fn test_13_2_04() { + let expr= r#"f|E"#; + test_braille("French", expr, "⠋⠿⠨⠑"); +} + +#[test] +fn test_14_1_01() { + let expr= r#"f(x)"#; + test_braille("French", expr, "⠋⠦⠭⠴"); } #[test] -fn lim_14_2_3() { - let expr= r#"limx→+∞ - ln(x+2)x=0"#; - test_braille("French", expr, " ⠇⠊⠍⠢⠢⠰⠭⠸⠱⠖⠘⠉⠆⠇⠝⠦⠭⠖⠣⠴⠌⠭⠶⠼"); +fn test_14_1_02() { + let expr= r#"g′(x)=3x+4"#; + test_braille("French", expr, "⠛⠄⠦⠭⠴⠶⠩⠭⠖⠹"); } #[test] -fn integral_14_4_3() { - let expr= r#"∯Sf(x,y)dxdy"#; +fn test_14_1_03() { + let expr= r#"f∘g(x)=f[g(x)]"#; + test_braille("French", expr, "⠋⠸⠼⠛⠦⠭⠴⠶⠋⠷⠛⠦⠭⠴⠾"); +} + +#[test] +fn test_14_1_04() { + let expr= r#"∂f(x,y)∂x=5xy−7x"#; + test_braille("French", expr, "⠐⠙⠋⠦⠭⠂⠽⠴⠌⠐⠙⠭⠶⠱⠭⠽⠤⠻⠭"); +} + +#[test] +fn test_14_2_01_corrected() { + let expr= r#"limx→+∞f(x)=0"#; + test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠖⠘⠉⠆⠋⠦⠭⠴⠶⠼"); +} + +#[test] +fn test_14_2_02_corrected_1() { + let expr= r#"limx→4x>4f(x)=−∞"#; + test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠹⠂⠭⠐⠜⠹⠆⠋⠦⠭⠴⠶⠤⠘⠉"); +} + +#[test] +fn test_14_2_02_corrected_2() { + let expr= r#"limx→4x>4f(x)=−∞"#; + test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠹⠂⠭⠐⠜⠹⠆⠋⠦⠭⠴⠶⠤⠘⠉"); +} + +#[test] +fn test_14_2_03_corrected() { + let expr= r#"limx→+∞ln⁡(x+2)x=0"#; + test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠖⠘⠉⠆⠰⠇⠝⠦⠭⠖⠣⠴⠌⠭⠆⠶⠼"); +} + +#[test] +fn test_14_4_01() { + let expr= r#"∫abf(x)dx"#; + test_braille("French", expr, "⠯⠢⠁⠈⠃⠋⠦⠭⠴⠙⠭"); +} + +#[test] +fn test_14_4_03() { + let expr= r#"∫125xdx"#; + test_braille("French", expr, "⠯⠢⠡⠈⠣⠰⠱⠭⠆⠙⠭"); +} + +#[test] +fn test_14_4_04() { + let expr= r#"∫(c)f(x)dx"#; + test_braille("French", expr, "⠯⠢⠦⠨⠉⠴⠋⠦⠭⠴⠙⠭"); +} + +#[test] +fn test_14_4_05() { + let expr= r#"∯Sf(x,y)dxdy"#; test_braille("French", expr, "⠨⠯⠯⠢⠨⠎⠋⠦⠭⠂⠽⠴⠙⠭⠙⠽"); } #[test] -fn log_14_5_1() { - let expr= r#"lnab"#; +fn test_14_5_01() { + let expr= r#"ln⁡(ab)"#; test_braille("French", expr, "⠇⠝⠦⠁⠌⠃⠴"); } #[test] -fn log_14_5_3() { - let expr= r#"log7x= - lnxln7"#; +fn test_14_5_02() { + let expr= r#"ln⁡xy=ln⁡x+ln⁡y"#; + test_braille("French", expr, "⠇⠝⠰⠭⠽⠆⠶⠇⠝⠰⠭⠆⠖⠇⠝⠰⠽⠆"); +} + +#[test] +fn test_14_5_03() { + let expr= r#"log7x=ln⁡xln⁡7"#; test_braille("French", expr, "⠇⠕⠛⠢⠻⠰⠭⠆⠶⠇⠝⠰⠭⠆⠌⠇⠝⠰⠻⠆"); } #[test] -fn symbols_15_1() { - let expr= r#"∃x:x∈A"#; +fn test_14_6_01() { + let expr= r#"cos⁡(a+b)=cosacosb−sinasinb"#; + test_braille("French", expr, "⠉⠕⠎⠦⠁⠖⠃⠴⠶⠉⠕⠎⠰⠁⠆⠉⠕⠎⠰⠃⠆⠐⠤⠎⠊⠝⠰⠁⠆⠎⠊⠝⠰⠃⠆"); +} + +#[test] +fn test_14_6_01b() { + let expr= r#"cos⁡(a+b)=cosacosb−sinasinb"#; + test_braille("French", expr, "⠨⠬⠦⠁⠖⠃⠴⠶⠨⠬⠁⠨⠬⠃⠤⠬⠁⠬⠃"); +} + +#[test] +fn test_14_7_01() { + let expr= r#"cos⁡h2x−sin⁡h2x=1"#; + test_braille("French", expr, "⠉⠓⠈⠣⠰⠭⠆⠤⠎⠓⠈⠣⠰⠭⠆⠶⠡"); +} + +#[test] +fn test_15_01() { + let expr= r#"∃x:x∈A"#; test_braille("French", expr, "⠸⠡⠭⠒⠭⠘⠡⠨⠁"); } + +#[test] +fn test_16_4_07() { + let expr= r#"(p+q)ième"#; + test_braille("French", expr, "⠦⠏⠖⠟⠴⠈⠊⠮⠍"); +} + From c7eda2c2ba66bcf9509a5231c418a48627c1acc3 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Fri, 5 Jun 2026 23:49:06 -0700 Subject: [PATCH 005/120] Updated to main. Needed to update the format of the tests to match the others. --- src/braille.rs | 8 +- tests/braille/French/chemistry.rs | 31 +- tests/braille/French/french.rs | 481 ++++++++++++++++++------------ 3 files changed, 311 insertions(+), 209 deletions(-) diff --git a/src/braille.rs b/src/braille.rs index d42a3a976..348deb7b0 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -2212,10 +2212,8 @@ static FRENCH_INDICATOR_REPLACEMENTS: phf::Map<&str, &str> = phf_map! { fn french_cleanup(pref_manager: Ref, raw_braille: String) -> String { // FIX: need to implement this -- this is just a copy of the Vietnam code - lazy_static! { - // Empty bases are ok if they follow whitespace - static ref EMPTY_BASE: Regex = Regex::new(r"(^|[W𝐖w])E").unwrap(); - } + // Empty bases are ok if they follow whitespace + static EMPTY_BASE: LazyLock = LazyLock::new(|| Regex::new(r"(^|[W𝐖w])E").unwrap()); debug!("french_cleanup: start={}", raw_braille); // let result = typeface_to_word_mode(&raw_braille); // let result = capitals_to_word_mode(&result); @@ -2238,7 +2236,7 @@ fn french_cleanup(pref_manager: Ref, raw_braille: String) -> let result = EMPTY_BASE.replace_all(&result, "$1"); - let result = REPLACE_INDICATORS.replace_all(&result, |cap: &Captures| { + let result: Cow<'_, str> = REPLACE_INDICATORS.replace_all(&result, |cap: &Captures| { let matched_char = &cap[0]; match matched_char { "𝔹" => &double_struck, diff --git a/tests/braille/French/chemistry.rs b/tests/braille/French/chemistry.rs index 4af951eda..b5d4606a4 100644 --- a/tests/braille/French/chemistry.rs +++ b/tests/braille/French/chemistry.rs @@ -2,46 +2,53 @@ // These tests are from the French braille authority's publication "NOTATION PIATHÉMATIQUE BRAILLE" (Première édition janvier 2007) // https://www.avh.asso.fr/sites/default/files/notation_mathematique_braille2_0.pdf use crate::common::*; +use anyhow::Result; #[test] -fn bond_I_4_1() { +fn bond_I_4_1() -> Result<()> { let expr= r#"H-H"#; - test_braille("French", expr, "⠨⠓⠤⠨⠓"); + test_braille("French", expr, "⠨⠓⠤⠨⠓")?; + return Ok(()); } #[test] -fn bond_I_4_2() { +fn bond_I_4_2() -> Result<()> { let expr= r#"O=O"#; - test_braille("French", expr, "⠨⠕⠶⠨⠕"); + test_braille("French", expr, "⠨⠕⠶⠨⠕")?; + return Ok(()); } #[test] -fn ion_II_1_3() { +fn ion_II_1_3() -> Result<()> { let expr= r#"SO42-"#; - test_braille("French", expr, "⠨⠎⠨⠕⠢⠹⠈⠰⠣⠤⠆"); + test_braille("French", expr, "⠨⠎⠨⠕⠢⠹⠈⠰⠣⠤⠆")?; + return Ok(()); } #[test] -fn equilibrium_III_5_1() { +fn equilibrium_III_5_1() -> Result<()> { let expr= r#"N2(g)+3H2(g)⇌2NH3(g)"#; - test_braille("French", expr, "⠨⠝⠣⠦⠛⠴⠖⠩⠨⠓⠣⠦⠛⠴⠸⠻⠣⠨⠝⠨⠓⠩⠦⠛⠴"); + test_braille("French", expr, "⠨⠝⠣⠦⠛⠴⠖⠩⠨⠓⠣⠦⠛⠴⠸⠻⠣⠨⠝⠨⠓⠩⠦⠛⠴")?; + return Ok(()); } #[test] -fn equilibrium_III_5_2() { +fn equilibrium_III_5_2() -> Result<()> { let expr= r#"2H2O ⇌(2)(1)H3O++OH-"#; - test_braille("French", expr, " ⠣⠨⠓⠣⠨⠕⠰⠸⠻⠆⠢⠢⠦⠣⠴⠈⠈⠦⠡⠴⠨⠓⠩⠨⠕⠈⠰⠖⠆⠖⠨⠕⠨⠓⠈⠰⠤⠆"); + test_braille("French", expr, " ⠣⠨⠓⠣⠨⠕⠰⠸⠻⠆⠢⠢⠦⠣⠴⠈⠈⠦⠡⠴⠨⠓⠩⠨⠕⠈⠰⠖⠆⠖⠨⠕⠨⠓⠈⠰⠤⠆")?; + return Ok(()); } #[test] -fn equilibrium_III_5_3() { +fn equilibrium_III_5_3() -> Result<()> { let expr= r#"R-CO-O- H+R'-OH ⇌hydrolyseestérification R-CO-O- R'+ H2O"#; - test_braille("French", expr, " ⠨⠗⠤⠨⠉⠨⠕⠤⠨⠕⠤⠨⠓⠖⠨⠗⠄⠤⠨⠕⠨⠓ ⠸⠻⠨⠢⠰⠓⠽⠙⠗⠕⠇⠽⠎⠑⠆⠨⠔⠈⠰⠑⠎⠞⠗⠊⠋⠊⠉⠁⠞⠊⠕⠝⠆⠨⠗⠤⠨⠉⠨⠕⠤⠨⠕⠤⠨⠗⠄⠖⠨⠓⠢⠣⠨⠕"); + test_braille("French", expr, " ⠨⠗⠤⠨⠉⠨⠕⠤⠨⠕⠤⠨⠓⠖⠨⠗⠄⠤⠨⠕⠨⠓ ⠸⠻⠨⠢⠰⠓⠽⠙⠗⠕⠇⠽⠎⠑⠆⠨⠔⠈⠰⠑⠎⠞⠗⠊⠋⠊⠉⠁⠞⠊⠕⠝⠆⠨⠗⠤⠨⠉⠨⠕⠤⠨⠕⠤⠨⠗⠄⠖⠨⠓⠢⠣⠨⠕")?; + return Ok(()); } diff --git a/tests/braille/French/french.rs b/tests/braille/French/french.rs index 0e6fb55be..0acd5bf56 100644 --- a/tests/braille/French/french.rs +++ b/tests/braille/French/french.rs @@ -4,587 +4,684 @@ // https://www.avh.asso.fr/sites/default/files/notation_mathematique_braille2_0.pdf use crate::common::*; +use anyhow::Result; #[test] -fn set_of_elements_6_4_01() { +fn set_of_elements_6_4_01() -> Result<()> { let expr= r#"E={a,b,c,d}"#; - test_braille("French", expr, "⠨⠑⠶⠨⠦⠁⠂⠃⠂⠉⠂⠙⠨⠴"); + test_braille("French", expr, "⠨⠑⠶⠨⠦⠁⠂⠃⠂⠉⠂⠙⠨⠴")?; + return Ok(()); } #[test] -fn set_r_definition_6_4_02() { +fn set_r_definition_6_4_02() -> Result<()> { let expr= r#"ℝ*=]−∞,0[∪]0,+∞["#; - test_braille("French", expr, "⠨⠨⠗⠈⠐⠔⠶⠾⠤⠘⠉⠂⠼⠷⠸⠖⠾⠼⠂⠖⠘⠉⠷"); + test_braille("French", expr, "⠨⠨⠗⠈⠐⠔⠶⠾⠤⠘⠉⠂⠼⠷⠸⠖⠾⠼⠂⠖⠘⠉⠷")?; + return Ok(()); } #[test] -fn set_belonging_6_4_03() { +fn set_belonging_6_4_03() -> Result<()> { let expr= r#"x∈[0,1;2,3]"#; - test_braille("French", expr, "⠭⠘⠡⠷⠼⠂⠡⠆⠣⠂⠩⠾"); + test_braille("French", expr, "⠭⠘⠡⠷⠼⠂⠡⠆⠣⠂⠩⠾")?; + return Ok(()); } #[test] -fn set_not_belong_tor_6_4_04() { +fn set_not_belong_tor_6_4_04() -> Result<()> { let expr= r#"x∉ℝ+"#; - test_braille("French", expr, "⠭⠘⠌⠨⠨⠗⠈⠖"); + test_braille("French", expr, "⠭⠘⠌⠨⠨⠗⠈⠖")?; + return Ok(()); } #[test] -fn set_included_6_4_05() { +fn set_included_6_4_05() -> Result<()> { let expr= r#"D⊂F"#; - test_braille("French", expr, "⠨⠙⠨⠡⠨⠋"); + test_braille("French", expr, "⠨⠙⠨⠡⠨⠋")?; + return Ok(()); } #[test] -fn set_not_included_6_4_06() { +fn set_not_included_6_4_06() -> Result<()> { let expr= r#"D⊄E"#; - test_braille("French", expr, "⠨⠙⠨⠌⠨⠑"); + test_braille("French", expr, "⠨⠙⠨⠌⠨⠑")?; + return Ok(()); } #[test] -fn set_included_or_equal_6_4_07() { +fn set_included_or_equal_6_4_07() -> Result<()> { let expr= r#"A⊆B"#; - test_braille("French", expr, "⠨⠁⠸⠨⠡⠨⠃"); + test_braille("French", expr, "⠨⠁⠸⠨⠡⠨⠃")?; + return Ok(()); } #[test] -fn set_intersection_6_4_08() { +fn set_intersection_6_4_08() -> Result<()> { let expr= r#"A∩B=⌀"#; - test_braille("French", expr, "⠨⠁⠘⠖⠨⠃⠶⠘⠼"); + test_braille("French", expr, "⠨⠁⠘⠖⠨⠃⠶⠘⠼")?; + return Ok(()); } #[test] -fn complement_of_6_4_09() { +fn complement_of_6_4_09() -> Result<()> { let expr= r#"∁EF"#; - test_braille("French", expr, "⠨⠩⠢⠨⠑⠨⠋"); + test_braille("French", expr, "⠨⠩⠢⠨⠑⠨⠋")?; + return Ok(()); } #[test] -fn fraction_01_7_01() { +fn fraction_01_7_01() -> Result<()> { let expr= r#"23=46"#; - test_braille("French", expr, "⠣⠌⠩⠶⠹⠌⠫"); + test_braille("French", expr, "⠣⠌⠩⠶⠹⠌⠫")?; + return Ok(()); } #[test] -fn devided_by_7_02() { +fn devided_by_7_02() -> Result<()> { let expr= r#"1÷2=0,5"#; - test_braille("French", expr, "⠡⠒⠣⠶⠼⠂⠱"); + test_braille("French", expr, "⠡⠒⠣⠶⠼⠂⠱")?; + return Ok(()); } #[test] -fn fraction_02_8_01() { +fn fraction_02_8_01() -> Result<()> { let expr= r#"a+ba"#; - test_braille("French", expr, "⠠⠄⠰⠁⠖⠃⠆⠌⠁"); + test_braille("French", expr, "⠠⠄⠰⠁⠖⠃⠆⠌⠁")?; + return Ok(()); } #[test] -fn fraction_03_8_02() { +fn fraction_03_8_02() -> Result<()> { let expr= r#"a+bc+d"#; - test_braille("French", expr, "⠠⠄⠰⠁⠖⠃⠆⠌⠰⠉⠖⠙⠆"); + test_braille("French", expr, "⠠⠄⠰⠁⠖⠃⠆⠌⠰⠉⠖⠙⠆")?; + return Ok(()); } #[test] -fn fraction_04_8_03() { +fn fraction_04_8_03() -> Result<()> { let expr= r#"ab+cd=ad+bcbd"#; - test_braille("French", expr, "⠠⠄⠁⠌⠃⠖⠉⠌⠙⠶⠰⠁⠙⠖⠃⠉⠆⠌⠃⠙"); + test_braille("French", expr, "⠠⠄⠁⠌⠃⠖⠉⠌⠙⠶⠰⠁⠙⠖⠃⠉⠆⠌⠃⠙")?; + return Ok(()); } #[test] -fn cardinal_8_04() { +fn cardinal_8_04() -> Result<()> { let expr= r#"CardE=5"#; - test_braille("French", expr, "⠠⠄⠨⠉⠁⠗⠙⠰⠨⠑⠆⠶⠱"); + test_braille("French", expr, "⠠⠄⠨⠉⠁⠗⠙⠰⠨⠑⠆⠶⠱")?; + return Ok(()); } #[test] -fn exponential_01_8_05() { +fn exponential_01_8_05() -> Result<()> { let expr= r#"ex+3"#; - test_braille("French", expr, "⠠⠄⠑⠈⠰⠭⠖⠩⠆"); + test_braille("French", expr, "⠠⠄⠑⠈⠰⠭⠖⠩⠆")?; + return Ok(()); } #[test] -fn exponential_02_8_06() { +fn exponential_02_8_06() -> Result<()> { let expr= r#"e7"#; - test_braille("French", expr, "⠠⠑⠈⠻"); + test_braille("French", expr, "⠠⠑⠈⠻")?; + return Ok(()); } #[test] -fn exponential_03_8_07() { +fn exponential_03_8_07() -> Result<()> { let expr= r#"e−x"#; - test_braille("French", expr, "⠠⠑⠈⠤⠭"); + test_braille("French", expr, "⠠⠑⠈⠤⠭")?; + return Ok(()); } #[test] -fn exponential_04_8_08() { +fn exponential_04_8_08() -> Result<()> { let expr= r#"e−π"#; - test_braille("French", expr, "⠠⠑⠈⠤⠘⠏"); + test_braille("French", expr, "⠠⠑⠈⠤⠘⠏")?; + return Ok(()); } #[test] -fn power_negativ_one_8_09() { +fn power_negativ_one_8_09() -> Result<()> { let expr= r#"x−1=1x"#; - test_braille("French", expr, "⠠⠭⠈⠤⠡⠶⠡⠌⠭"); + test_braille("French", expr, "⠠⠭⠈⠤⠡⠶⠡⠌⠭")?; + return Ok(()); } #[test] -fn prime_9_1_01() { +fn prime_9_1_01() -> Result<()> { let expr= r#"a′"#; - test_braille("French", expr, "⠁⠄"); + test_braille("French", expr, "⠁⠄")?; + return Ok(()); } #[test] -fn prime_dople_9_1_02() { +fn prime_dople_9_1_02() -> Result<()> { let expr= r#"x″"#; - test_braille("French", expr, "⠭⠄⠄"); + test_braille("French", expr, "⠭⠄⠄")?; + return Ok(()); } #[test] -fn prime_dople_triple_9_1_03() { +fn prime_dople_triple_9_1_03() -> Result<()> { let expr= r#"a′x+a″y+a‴z"#; - test_braille("French", expr, "⠁⠄⠭⠖⠁⠄⠄⠽⠖⠁⠄⠄⠄⠵"); + test_braille("French", expr, "⠁⠄⠭⠖⠁⠄⠄⠽⠖⠁⠄⠄⠄⠵")?; + return Ok(()); } #[test] -fn sub_a_p_9_2_01() { +fn sub_a_p_9_2_01() -> Result<()> { let expr= r#"ap"#; - test_braille("French", expr, "⠁⠢⠏"); + test_braille("French", expr, "⠁⠢⠏")?; + return Ok(()); } #[test] -fn sub_x_0__9_2_02() { +fn sub_x_0__9_2_02() -> Result<()> { let expr= r#"x0"#; - test_braille("French", expr, "⠭⠢⠼"); + test_braille("French", expr, "⠭⠢⠼")?; + return Ok(()); } #[test] -fn sub_u_n_plus_one_9_2_03() { +fn sub_u_n_plus_one_9_2_03() -> Result<()> { let expr= r#"un+1"#; - test_braille("French", expr, "⠥⠢⠰⠝⠖⠡⠆"); + test_braille("French", expr, "⠥⠢⠰⠝⠖⠡⠆")?; + return Ok(()); } #[test] -fn sup_x_2_9_3_01() { +fn sup_x_2_9_3_01() -> Result<()> { let expr= r#"x2"#; - test_braille("French", expr, "⠭⠈⠣"); + test_braille("French", expr, "⠭⠈⠣")?; + return Ok(()); } #[test] -fn sup_x_p_plus_q_9_3_02() { +fn sup_x_p_plus_q_9_3_02() -> Result<()> { let expr= r#"xp+q"#; - test_braille("French", expr, "⠭⠈⠰⠏⠖⠟⠆"); + test_braille("French", expr, "⠭⠈⠰⠏⠖⠟⠆")?; + return Ok(()); } #[test] -fn sup_x_negative_number_9_3_03() { +fn sup_x_negative_number_9_3_03() -> Result<()> { let expr= r#"a−5,3"#; - test_braille("French", expr, "⠁⠈⠤⠱⠂⠩"); + test_braille("French", expr, "⠁⠈⠤⠱⠂⠩")?; + return Ok(()); } #[test] -fn sup_a_pi_9_3_04() { +fn sup_a_pi_9_3_04() -> Result<()> { let expr= r#"aπ"#; - test_braille("French", expr, "⠁⠈⠘⠏"); + test_braille("French", expr, "⠁⠈⠘⠏")?; + return Ok(()); } #[test] -fn sup_5_formula_block_1_9_3_05() { +fn sup_5_formula_block_1_9_3_05() -> Result<()> { let expr= r#"523≠523"#; - test_braille("French", expr, "⠱⠈⠣⠜⠩⠨⠶⠱⠈⠰⠣⠜⠩⠆"); + test_braille("French", expr, "⠱⠈⠣⠜⠩⠨⠶⠱⠈⠰⠣⠜⠩⠆")?; + return Ok(()); } #[test] -fn sup_5_formula_block_0_9_3_06() { +fn sup_5_formula_block_0_9_3_06() -> Result<()> { let expr= r#"e−5x≠e−5x"#; - test_braille("French", expr, "⠑⠈⠰⠤⠱⠭⠆⠨⠶⠰⠑⠈⠤⠱⠆⠭"); + test_braille("French", expr, "⠑⠈⠰⠤⠱⠭⠆⠨⠶⠰⠑⠈⠤⠱⠆⠭")?; + return Ok(()); } #[test] -fn sub_a_prime_0_9_4_01() { +fn sub_a_prime_0_9_4_01() -> Result<()> { let expr= r#"a′0"#; - test_braille("French", expr, "⠁⠄⠢⠼"); + test_braille("French", expr, "⠁⠄⠢⠼")?; + return Ok(()); } #[test] -fn sub_mixt_0_9_4_02() { +fn sub_mixt_0_9_4_02() -> Result<()> { let expr= r#"x0′2"#; - test_braille("French", expr, "⠭⠄⠢⠼⠈⠣"); + test_braille("French", expr, "⠭⠄⠢⠼⠈⠣")?; + return Ok(()); } #[test] -fn choose_9_4_03() { +fn choose_9_4_03() -> Result<()> { let expr= r#"∁mp=∁mm−p"#; - test_braille("French", expr, "⠨⠉⠢⠍⠈⠏⠶⠨⠉⠢⠍⠈⠰⠍⠤⠏⠆"); + test_braille("French", expr, "⠨⠉⠢⠍⠈⠏⠶⠨⠉⠢⠍⠈⠰⠍⠤⠏⠆")?; + return Ok(()); } #[test] -fn sub_sup_mixed_formulaq_9_4_04() { +fn sub_sup_mixed_formulaq_9_4_04() -> Result<()> { let expr= r#"am+n′p+q"#; - test_braille("French", expr, "⠁⠄⠄⠢⠰⠍⠖⠝⠆⠈⠰⠏⠖⠟⠆"); + test_braille("French", expr, "⠁⠄⠄⠢⠰⠍⠖⠝⠆⠈⠰⠏⠖⠟⠆")?; + return Ok(()); } #[test] -fn sub_sup_operators_9_4_05() { +fn sub_sup_operators_9_4_05() -> Result<()> { let expr= r#"ℝ+*"#; - test_braille("French", expr, "⠨⠨⠗⠢⠖⠈⠐⠔"); + test_braille("French", expr, "⠨⠨⠗⠢⠖⠈⠐⠔")?; + return Ok(()); } #[test] -fn left_sup_9_5_01() { +fn left_sup_9_5_01() -> Result<()> { let expr= r#"tA"#; - test_braille("French", expr, "⠈⠞⠨⠁"); + test_braille("French", expr, "⠈⠞⠨⠁")?; + return Ok(()); } #[test] -fn lef_sub_9_5_02() { +fn lef_sub_9_5_02() -> Result<()> { let expr= r#"2w"#; - test_braille("French", expr, "⠢⠣⠺"); + test_braille("French", expr, "⠢⠣⠺")?; + return Ok(()); } #[test] -fn left_right_sup_9_5_03() { +fn left_right_sup_9_5_03() -> Result<()> { let expr= r#"tAtB"#; - test_braille("French", expr, "⠰⠈⠞⠨⠁⠆⠰⠈⠞⠨⠃⠆"); + test_braille("French", expr, "⠰⠈⠞⠨⠁⠆⠰⠈⠞⠨⠃⠆")?; + return Ok(()); } #[test] -fn left_sub_sup_test_9_5_04() { +fn left_sub_sup_test_9_5_04() -> Result<()> { let expr= r#"816O"#; - test_braille("French", expr, "⠨⠕⠠⠢⠳⠠⠈⠡⠫"); + test_braille("French", expr, "⠨⠕⠠⠢⠳⠠⠈⠡⠫")?; + return Ok(()); } #[test] -fn bigsum_one_line_9_6_01() { +fn bigsum_one_line_9_6_01() -> Result<()> { let expr= r#"∑i=1nui=u1+u2+⋯+un"#; - test_braille("French", expr, "⠨⠘⠎⠢⠢⠰⠊⠶⠡⠆⠈⠈⠝⠥⠢⠊⠶⠥⠢⠡⠖⠥⠢⠣⠖⠲⠲⠲⠖⠥⠢⠝"); + test_braille("French", expr, "⠨⠘⠎⠢⠢⠰⠊⠶⠡⠆⠈⠈⠝⠥⠢⠊⠶⠥⠢⠡⠖⠥⠢⠣⠖⠲⠲⠲⠖⠥⠢⠝")?; + return Ok(()); } #[test] -fn bigsum_two_lines_9_6_01() { +fn bigsum_two_lines_9_6_01() -> Result<()> { let expr= r#"∑i=1nui=u1+u2+⋯+un"#; test_braille("French", expr, " ⠨⠘⠎⠢⠢⠰⠊⠶⠡⠆⠈⠈⠝⠥⠢⠊⠐ -⠶⠥⠢⠡⠖⠥⠢⠣⠖⠲⠲⠲⠖⠥⠢⠝"); +⠶⠥⠢⠡⠖⠥⠢⠣⠖⠲⠲⠲⠖⠥⠢⠝")?; + return Ok(()); } #[test] -fn bigsum_one_line_with_bloc_9_6_01() { +fn bigsum_one_line_with_bloc_9_6_01() -> Result<()> { let expr= r#"∑i=1nui=u1+u2+⋯+un"#; - test_braille("French", expr, "⠨⠘⠎⠢⠢⠰⠊⠶⠡⠆⠈⠈⠝⠥⠢⠊⠶⠥⠢⠡⠖⠥⠢⠣⠖⠲⠲⠲⠖⠥⠢⠝"); + test_braille("French", expr, "⠨⠘⠎⠢⠢⠰⠊⠶⠡⠆⠈⠈⠝⠥⠢⠊⠶⠥⠢⠡⠖⠥⠢⠣⠖⠲⠲⠲⠖⠥⠢⠝")?; + return Ok(()); } #[test] -fn root_square_10_01() { +fn root_square_10_01() -> Result<()> { let expr= r#"2"#; - test_braille("French", expr, "⠜⠣"); + test_braille("French", expr, "⠜⠣")?; + return Ok(()); } #[test] -fn root_fourth_10_02() { +fn root_fourth_10_02() -> Result<()> { let expr= r#"a4"#; - test_braille("French", expr, "⠈⠹⠜⠁"); + test_braille("French", expr, "⠈⠹⠜⠁")?; + return Ok(()); } #[test] -fn square_formula_1_10_03() { +fn square_formula_1_10_03() -> Result<()> { let expr= r#"(a+b)36=a+b"#; - test_braille("French", expr, "⠈⠫⠜⠰⠦⠁⠖⠃⠴⠈⠩⠆⠶⠜⠰⠁⠖⠃⠆"); + test_braille("French", expr, "⠈⠫⠜⠰⠦⠁⠖⠃⠴⠈⠩⠆⠶⠜⠰⠁⠖⠃⠆")?; + return Ok(()); } #[test] -fn square_formula_2_10_04() { +fn square_formula_2_10_04() -> Result<()> { let expr= r#"a+a+b3"#; - test_braille("French", expr, "⠈⠩⠜⠰⠁⠖⠜⠰⠁⠖⠃⠆⠆"); + test_braille("French", expr, "⠈⠩⠜⠰⠁⠖⠜⠰⠁⠖⠃⠆⠆")?; + return Ok(()); } #[test] -fn square_formula_3_10_05() { +fn square_formula_3_10_05() -> Result<()> { let expr= r#"a4b3"#; - test_braille("French", expr, "⠰⠈⠹⠜⠁⠆⠰⠈⠩⠜⠃⠆"); + test_braille("French", expr, "⠰⠈⠹⠜⠁⠆⠰⠈⠩⠜⠃⠆")?; + return Ok(()); } #[test] -fn function_from_r_to_r_11_01() { +fn function_from_r_to_r_11_01() -> Result<()> { let expr= r#"f:ℝ→ℝ,x↦y=1x"#; - test_braille("French", expr, "⠒⠨⠨⠗⠸⠱⠨⠨⠗⠭⠐⠱⠽⠶⠡⠌⠭"); + test_braille("French", expr, "⠒⠨⠨⠗⠸⠱⠨⠨⠗⠭⠐⠱⠽⠶⠡⠌⠭")?; + return Ok(()); } #[test] -fn over_arrow_12_1_01() { +fn over_arrow_12_1_01() -> Result<()> { let expr= r#"v→"#; - test_braille("French", expr, "⠨⠒⠧"); + test_braille("French", expr, "⠨⠒⠧")?; + return Ok(()); } #[test] -fn over_barre_12_1_02() { +fn over_barre_12_1_02() -> Result<()> { let expr= r#"AB¯"#; - test_braille("French", expr, "⠸⠒⠨⠁⠨⠃"); + test_braille("French", expr, "⠸⠒⠨⠁⠨⠃")?; + return Ok(()); } #[test] -fn over_arc_12_1_03() { +fn over_arc_12_1_03() -> Result<()> { let expr= r#"AB⏜"#; - test_braille("French", expr, "⠈⠒⠨⠁⠨⠃"); + test_braille("French", expr, "⠈⠒⠨⠁⠨⠃")?; + return Ok(()); } #[test] -fn over_large_hat_1_12_1_04() { +fn over_large_hat_1_12_1_04() -> Result<()> { let expr= r#"AOB̂"#; - test_braille("French", expr, "⠘⠒⠨⠁⠨⠕⠨⠃"); + test_braille("French", expr, "⠘⠒⠨⠁⠨⠕⠨⠃")?; + return Ok(()); } #[test] -fn over_large_hat_2_12_1_05() { +fn over_large_hat_2_12_1_05() -> Result<()> { let expr= r#"(Ox,Oy)̂"#; - test_braille("French", expr, "⠘⠒⠦⠨⠕⠭⠂⠨⠕⠽⠴"); + test_braille("French", expr, "⠘⠒⠦⠨⠕⠭⠂⠨⠕⠽⠴")?; + return Ok(()); } #[test] -fn over_large_hat_3_12_1_06() { +fn over_large_hat_3_12_1_06() -> Result<()> { let expr= r#"(OA→,OM→)̂"#; - test_braille("French", expr, "⠘⠒⠦⠨⠒⠨⠕⠨⠁⠂⠨⠒⠨⠕⠨⠍⠴"); + test_braille("French", expr, "⠘⠒⠦⠨⠒⠨⠕⠨⠁⠂⠨⠒⠨⠕⠨⠍⠴")?; + return Ok(()); } #[test] -fn parallel_d_delta_12_3_01() { +fn parallel_d_delta_12_3_01() -> Result<()> { let expr= r#"(D)⫽(Δ)"#; - test_braille("French", expr, "⠦⠨⠙⠴⠸⠳⠦⠨⠘⠙⠴"); + test_braille("French", expr, "⠦⠨⠙⠴⠸⠳⠦⠨⠘⠙⠴")?; + return Ok(()); } #[test] -fn perpendicular_12_3_02() { +fn perpendicular_12_3_02() -> Result<()> { let expr= r#"(AB)⊥(A′B′)"#; - test_braille("French", expr, "⠦⠨⠁⠨⠃⠴⠘⠳⠦⠨⠁⠄⠨⠃⠄⠴"); + test_braille("French", expr, "⠦⠨⠁⠨⠃⠴⠘⠳⠦⠨⠁⠄⠨⠃⠄⠴")?; + return Ok(()); } #[test] -fn abs_13_2_01() { +fn abs_13_2_01() -> Result<()> { let expr= r#"|x0|"#; - test_braille("French", expr, "⠿⠭⠢⠼⠿"); + test_braille("French", expr, "⠿⠭⠢⠼⠿")?; + return Ok(()); } #[test] -fn modulus_13_2_02() { +fn modulus_13_2_02() -> Result<()> { let expr= r#"|a+ib|"#; - test_braille("French", expr, "⠿⠁⠖⠊⠃⠿"); + test_braille("French", expr, "⠿⠁⠖⠊⠃⠿")?; + return Ok(()); } #[test] -fn norm_k_u_vector_13_2_03() { +fn norm_k_u_vector_13_2_03() -> Result<()> { let expr= r#"∥ku→∥=|k|∥u→∥"#; - test_braille("French", expr, "⠘⠿⠅⠨⠒⠥⠘⠿⠶⠿⠅⠿⠘⠿⠨⠒⠥⠘⠿"); + test_braille("French", expr, "⠘⠿⠅⠨⠒⠥⠘⠿⠶⠿⠅⠿⠘⠿⠨⠒⠥⠘⠿")?; + return Ok(()); } #[test] -fn restriction_of_f_to_13_2_04() { +fn restriction_of_f_to_13_2_04() -> Result<()> { let expr= r#"f|E"#; - test_braille("French", expr, "⠋⠿⠨⠑"); + test_braille("French", expr, "⠋⠿⠨⠑")?; + return Ok(()); } #[test] -fn determinant_tabular_13_3_01() { +fn determinant_tabular_13_3_01() -> Result<()> { let expr= r#"I=|1023|=3"#; test_braille("French", expr, " ⠨⠊⠶⠿⠡⠀⠼⠿ - ⠿⠣⠀⠩⠿⠶⠩"); + ⠿⠣⠀⠩⠿⠶⠩")?; + return Ok(()); } #[test] -fn matrix_tabular_13_3_02() { +fn matrix_tabular_13_3_02() -> Result<()> { let expr= r#"J=(142356−789)"#; test_braille("French", expr, " ⠨⠚⠶⠦⠡⠀⠀⠀⠀⠀⠀⠹⠴ ⠀⠀⠀⠦⠣⠀⠩⠀⠱⠀⠀⠫⠴ -⠀⠀⠀⠦⠤⠻⠀⠳⠀⠀⠀⠪⠴"); +⠀⠀⠀⠦⠤⠻⠀⠳⠀⠀⠀⠪⠴")?; + return Ok(()); } #[test] -fn matrix_linear_13_3_02() { +fn matrix_linear_13_3_02() -> Result<()> { let expr= r#"J=(142356−789)"#; - test_braille("French", expr, "⠨⠚⠶⠘⠦⠡⠀⠐⠂⠀⠐⠂⠀⠹⠀⠠⠜⠀⠣⠀⠩⠀⠱⠀⠫⠀⠠⠜⠀⠤⠻⠀⠳⠀⠐⠂⠀⠪⠘⠴"); + test_braille("French", expr, "⠨⠚⠶⠘⠦⠡⠀⠐⠂⠀⠐⠂⠀⠹⠀⠠⠜⠀⠣⠀⠩⠀⠱⠀⠫⠀⠠⠜⠀⠤⠻⠀⠳⠀⠐⠂⠀⠪⠘⠴")?; + return Ok(()); } #[test] -fn matrix_with_equations_two_lines_13_3_03() { +fn matrix_with_equations_two_lines_13_3_03() -> Result<()> { let expr= r#"K=[1x2+3x−96x3−x2+1x+3y+5g(x)]"#; test_braille("French", expr, " ⠨⠅⠶⠘⠷⠡⠀⠰⠭⠈⠣⠖⠩⠭⠤⠪⠆⠌⠰⠫⠭⠈⠩⠤⠭⠈⠣⠐ -⠖⠡⠆⠀⠠⠜⠀⠰⠭⠖⠩⠆⠌⠰⠽⠖⠱⠆⠀⠛⠦⠭⠴⠘⠾"); +⠖⠡⠆⠀⠠⠜⠀⠰⠭⠖⠩⠆⠌⠰⠽⠖⠱⠆⠀⠛⠦⠭⠴⠘⠾")?; + return Ok(()); } #[test] -fn matrix_with_equations_one_lines_13_3_03() { +fn matrix_with_equations_one_lines_13_3_03() -> Result<()> { let expr= r#"K=[1x2+3x−96x3−x2+1x+3y+5g(x)]"#; - test_braille("French", expr, "⠨⠅⠶⠘⠷⠡⠀⠰⠭⠈⠣⠖⠩⠭⠤⠪⠆⠌⠰⠫⠭⠈⠩⠤⠭⠈⠣⠖⠡⠆⠀⠠⠜⠀⠰⠭⠖⠩⠆⠌⠰⠽⠖⠱⠆⠀⠛⠦⠭⠴⠘⠾"); + test_braille("French", expr, "⠨⠅⠶⠘⠷⠡⠀⠰⠭⠈⠣⠖⠩⠭⠤⠪⠆⠌⠰⠫⠭⠈⠩⠤⠭⠈⠣⠖⠡⠆⠀⠠⠜⠀⠰⠭⠖⠩⠆⠌⠰⠽⠖⠱⠆⠀⠛⠦⠭⠴⠘⠾")?; + return Ok(()); } #[test] -fn sytem_equation_tabular_13_4_01() { +fn sytem_equation_tabular_13_4_01() -> Result<()> { let expr= r#"{2x+3y=53x−7y=8"#; test_braille("French", expr, " ⠸⠦⠣⠭⠖⠩⠽⠶⠱ -⠀⠀⠩⠭⠤⠻⠽⠶⠳"); +⠀⠀⠩⠭⠤⠻⠽⠶⠳")?; + return Ok(()); } #[test] -fn sytem_equation_linéaraire_13_4_01() { +fn sytem_equation_linéaraire_13_4_01() -> Result<()> { let expr= r#"{2x+3y=53x−7y=8"#; - test_braille("French", expr, "⠸⠦⠣⠭⠖⠩⠽⠶⠱⠀⠠⠜⠀⠩⠭⠤⠻⠽⠶⠳"); + test_braille("French", expr, "⠸⠦⠣⠭⠖⠩⠽⠶⠱⠀⠠⠜⠀⠩⠭⠤⠻⠽⠶⠳")?; + return Ok(()); } #[test] -fn function_f_of_x_14_1_01() { +fn function_f_of_x_14_1_01() -> Result<()> { let expr= r#"f(x)"#; - test_braille("French", expr, "⠋⠦⠭⠴"); + test_braille("French", expr, "⠋⠦⠭⠴")?; + return Ok(()); } #[test] -fn function_g_prime_14_1_02() { +fn function_g_prime_14_1_02() -> Result<()> { let expr= r#"g′(x)=3x+4"#; - test_braille("French", expr, "⠛⠄⠦⠭⠴⠶⠩⠭⠖⠹"); + test_braille("French", expr, "⠛⠄⠦⠭⠴⠶⠩⠭⠖⠹")?; + return Ok(()); } #[test] -fn function_composition_14_1_03() { +fn function_composition_14_1_03() -> Result<()> { let expr= r#"f∘g(x)=f[g(x)]"#; - test_braille("French", expr, "⠋⠸⠼⠛⠦⠭⠴⠶⠋⠷⠛⠦⠭⠴⠾"); + test_braille("French", expr, "⠋⠸⠼⠛⠦⠭⠴⠶⠋⠷⠛⠦⠭⠴⠾")?; + return Ok(()); } #[test] -fn function_partial_derivative_14_1_04() { +fn function_partial_derivative_14_1_04() -> Result<()> { let expr= r#"∂f(x,y)∂x=5xy−7x"#; - test_braille("French", expr, "⠐⠙⠋⠦⠭⠂⠽⠴⠌⠐⠙⠭⠶⠱⠭⠽⠤⠻⠭"); + test_braille("French", expr, "⠐⠙⠋⠦⠭⠂⠽⠴⠌⠐⠙⠭⠶⠱⠭⠽⠤⠻⠭")?; + return Ok(()); } #[test] -fn limit_x_tends_1_14_2_01_corrected() { +fn limit_x_tends_1_14_2_01_corrected() -> Result<()> { let expr= r#"limx→+∞f(x)=0"#; - test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠖⠘⠉⠆⠋⠦⠭⠴⠶⠼"); + test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠖⠘⠉⠆⠋⠦⠭⠴⠶⠼")?; + return Ok(()); } #[test] -fn limit_x_tends_and_x_lt_1_14_2_02_corrected_1() { +fn limit_x_tends_and_x_lt_1_14_2_02_corrected_1() -> Result<()> { let expr= r#"limx→4x>4f(x)=−∞"#; - test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠹⠂⠭⠐⠜⠹⠆⠋⠦⠭⠴⠶⠤⠘⠉"); + test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠹⠂⠭⠐⠜⠹⠆⠋⠦⠭⠴⠶⠤⠘⠉")?; + return Ok(()); } #[test] -fn limit_x_tends_and_x_lt_2_14_2_02_corrected_2() { +fn limit_x_tends_and_x_lt_2_14_2_02_corrected_2() -> Result<()> { let expr= r#"limx→4x>4f(x)=−∞"#; - test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠹⠂⠭⠐⠜⠹⠆⠋⠦⠭⠴⠶⠤⠘⠉"); + test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠹⠂⠭⠐⠜⠹⠆⠋⠦⠭⠴⠶⠤⠘⠉")?; + return Ok(()); } #[test] -fn limit_x_tends_of_formula_lt_2_14_2_03_corrected() { +fn limit_x_tends_of_formula_lt_2_14_2_03_corrected() -> Result<()> { let expr= r#"limx→+∞ln⁡(x+2)x=0"#; - test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠖⠘⠉⠆⠰⠇⠝⠦⠭⠖⠣⠴⠌⠭⠆⠶⠼"); + test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠖⠘⠉⠆⠰⠇⠝⠦⠭⠖⠣⠴⠌⠭⠆⠶⠼")?; + return Ok(()); } #[test] -fn sign_chart_14_3_01() { +fn sign_chart_14_3_01() -> Result<()> { let expr= r#"x−∞32+∞2x−3−0+"#; test_braille("French", expr, " ⠭⠀⠀⠀⠸⠤⠘⠉⠀⠀⠩⠌⠣⠀⠀⠀⠖⠘⠉ ⠒⠒⠒⠒⠺⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒ -⠣⠭⠤⠩⠸⠀⠀⠀⠤⠀⠀⠼⠀⠀⠖"); +⠣⠭⠤⠩⠸⠀⠀⠀⠤⠀⠀⠼⠀⠀⠖")?; + return Ok(()); } #[test] -fn sign_chart_14_3_02() { +fn sign_chart_14_3_02() -> Result<()> { let expr= r#"x−∞7+∞f′(x)+0−f(x)−∞↗−1↘−∞"#; test_braille("French", expr, " ⠀⠭⠀⠀⠀⠀⠸⠤⠘⠉⠀⠀⠀⠀⠀⠻⠀⠀⠀⠀⠀⠖⠘ ⠒⠒⠒⠒⠒⠺⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒ ⠋⠄⠦⠭⠴⠸⠀⠀⠀⠀⠖⠀⠀⠀⠀⠼⠀⠀⠀⠀⠤ -⠋⠦⠭⠴⠀⠸⠤⠘⠉⠀⠘⠱⠀⠤⠡⠀⠨⠱⠀⠤⠘⠉"); +⠋⠦⠭⠴⠀⠸⠤⠘⠉⠀⠘⠱⠀⠤⠡⠀⠨⠱⠀⠤⠘⠉")?; + return Ok(()); } #[test] -fn integral_from_to_1_14_4_01() { +fn integral_from_to_1_14_4_01() -> Result<()> { let expr= r#"∫abf(x)dx"#; - test_braille("French", expr, "⠯⠢⠁⠈⠃⠋⠦⠭⠴⠙⠭"); + test_braille("French", expr, "⠯⠢⠁⠈⠃⠋⠦⠭⠴⠙⠭")?; + return Ok(()); } #[test] -fn integral_two_lines_14_4_02() { +fn integral_two_lines_14_4_02() -> Result<()> { let expr= r#"∫052f(x)dx=F(52)−F(0)"#; test_braille("French", expr, " ⠯⠢⠼⠈⠰⠱⠌⠣⠆⠋⠦⠭⠴⠙⠭⠐ -⠶⠨⠋⠦⠱⠌⠣⠴⠤⠨⠋⠦⠼⠴"); +⠶⠨⠋⠦⠱⠌⠣⠴⠤⠨⠋⠦⠼⠴")?; + return Ok(()); } #[test] -fn integral_one_lines14_4_02() { +fn integral_one_lines14_4_02() -> Result<()> { let expr= r#"∫052f(x)dx=F(52)−F(0)"#; - test_braille("French", expr, "⠯⠢⠼⠈⠰⠱⠌⠣⠆⠋⠦⠭⠴⠙⠭⠶⠨⠋⠦⠱⠌⠣⠴⠤⠨⠋⠦⠼⠴"); + test_braille("French", expr, "⠯⠢⠼⠈⠰⠱⠌⠣⠆⠋⠦⠭⠴⠙⠭⠶⠨⠋⠦⠱⠌⠣⠴⠤⠨⠋⠦⠼⠴")?; + return Ok(()); } #[test] -fn integral_from_to_2_14_4_03() { +fn integral_from_to_2_14_4_03() -> Result<()> { let expr= r#"∫125xdx"#; - test_braille("French", expr, "⠯⠢⠡⠈⠣⠰⠱⠭⠆⠙⠭"); + test_braille("French", expr, "⠯⠢⠡⠈⠣⠰⠱⠭⠆⠙⠭")?; + return Ok(()); } #[test] -fn integral_over_curve_14_4_04() { +fn integral_over_curve_14_4_04() -> Result<()> { let expr= r#"∫(c)f(x)dx"#; - test_braille("French", expr, "⠯⠢⠦⠨⠉⠴⠋⠦⠭⠴⠙⠭"); + test_braille("French", expr, "⠯⠢⠦⠨⠉⠴⠋⠦⠭⠴⠙⠭")?; + return Ok(()); } #[test] -fn integral_double_over_surface_14_4_05() { +fn integral_double_over_surface_14_4_05() -> Result<()> { let expr= r#"∯Sf(x,y)dxdy"#; - test_braille("French", expr, "⠨⠯⠯⠢⠨⠎⠋⠦⠭⠂⠽⠴⠙⠭⠙⠽"); + test_braille("French", expr, "⠨⠯⠯⠢⠨⠎⠋⠦⠭⠂⠽⠴⠙⠭⠙⠽")?; + return Ok(()); } #[test] -fn logarithm_natural_fraction_14_5_01() { +fn logarithm_natural_fraction_14_5_01() -> Result<()> { let expr= r#"ln⁡(ab)"#; - test_braille("French", expr, "⠇⠝⠦⠁⠌⠃⠴"); + test_braille("French", expr, "⠇⠝⠦⠁⠌⠃⠴")?; + return Ok(()); } #[test] -fn logarithm_natural_product_14_5_02() { +fn logarithm_natural_product_14_5_02() -> Result<()> { let expr= r#"ln⁡xy=ln⁡x+ln⁡y"#; - test_braille("French", expr, "⠇⠝⠰⠭⠽⠆⠶⠇⠝⠰⠭⠆⠖⠇⠝⠰⠽⠆"); + test_braille("French", expr, "⠇⠝⠰⠭⠽⠆⠶⠇⠝⠰⠭⠆⠖⠇⠝⠰⠽⠆")?; + return Ok(()); } #[test] -fn logarthm_change_base_14_5_03() { +fn logarthm_change_base_14_5_03() -> Result<()> { let expr= r#"log7x=ln⁡xln⁡7"#; - test_braille("French", expr, "⠇⠕⠛⠢⠻⠰⠭⠆⠶⠇⠝⠰⠭⠆⠌⠇⠝⠰⠻⠆"); + test_braille("French", expr, "⠇⠕⠛⠢⠻⠰⠭⠆⠶⠇⠝⠰⠭⠆⠌⠇⠝⠰⠻⠆")?; + return Ok(()); } #[test] -fn cos_of_sum_1_14_6_01() { +fn cos_of_sum_1_14_6_01() -> Result<()> { let expr= r#"cos⁡(a+b)=cosacosb−sinasinb"#; - test_braille("French", expr, "⠉⠕⠎⠦⠁⠖⠃⠴⠶⠉⠕⠎⠰⠁⠆⠉⠕⠎⠰⠃⠆⠐⠤⠎⠊⠝⠰⠁⠆⠎⠊⠝⠰⠃⠆"); + test_braille("French", expr, "⠉⠕⠎⠦⠁⠖⠃⠴⠶⠉⠕⠎⠰⠁⠆⠉⠕⠎⠰⠃⠆⠐⠤⠎⠊⠝⠰⠁⠆⠎⠊⠝⠰⠃⠆")?; + return Ok(()); } #[test] -fn cos_of_sum_2_14_6_01b() { +fn cos_of_sum_2_14_6_01b() -> Result<()> { let expr= r#"cos⁡(a+b)=cosacosb−sinasinb"#; - test_braille("French", expr, "⠨⠬⠦⠁⠖⠃⠴⠶⠨⠬⠁⠨⠬⠃⠤⠬⠁⠬⠃"); + test_braille("French", expr, "⠨⠬⠦⠁⠖⠃⠴⠶⠨⠬⠁⠨⠬⠃⠤⠬⠁⠬⠃")?; + return Ok(()); } #[test] -fn cos_of_sum_3_14_6_01b() { +fn cos_of_sum_3_14_6_01b() -> Result<()> { let expr= r#"cos(a+b)=cosacosb−sinasinb"#; - test_braille("French", expr, "⠨⠬⠦⠁⠖⠃⠴⠶⠨⠬⠁⠨⠬⠃⠤⠬⠁⠬⠃"); + test_braille("French", expr, "⠨⠬⠦⠁⠖⠃⠴⠶⠨⠬⠁⠨⠬⠃⠤⠬⠁⠬⠃")?; + return Ok(()); } #[test] -fn cosh_sinh_14_7_01() { +fn cosh_sinh_14_7_01() -> Result<()> { let expr= r#"cosh2x−sinh2x=1"#; - test_braille("French", expr, "⠉⠓⠈⠣⠰⠭⠆⠤⠎⠓⠈⠣⠰⠭⠆⠶⠡"); + test_braille("French", expr, "⠉⠓⠈⠣⠰⠭⠆⠤⠎⠓⠈⠣⠰⠭⠆⠶⠡")?; + return Ok(()); } #[test] -fn exists_element_of_15_01() { +fn exists_element_of_15_01() -> Result<()> { let expr= r#"∃x:x∈A"#; - test_braille("French", expr, "⠸⠡⠭⠒⠭⠘⠡⠨⠁"); + test_braille("French", expr, "⠸⠡⠭⠒⠭⠘⠡⠨⠁")?; + return Ok(()); } #[test] -fn set_formula_15_02() { +fn set_formula_15_02() -> Result<()> { let expr= r#"∀ε>0∃η>0:|x2−x1|≤η⇒|f(x2)−f(x1)|≤ε"#; - test_braille("French", expr, "⠸⠌⠘⠑⠐⠜⠼⠀⠸⠡⠘⠓⠐⠜⠼⠒⠀⠿⠭⠢⠣⠤⠭⠢⠡⠿⠘⠣⠘⠓⠀⠒⠂⠀⠿⠋⠦⠭⠢⠣⠴⠤⠋⠦⠭⠢⠡⠴⠿⠘⠣⠘⠑"); + test_braille("French", expr, "⠸⠌⠘⠑⠐⠜⠼⠀⠸⠡⠘⠓⠐⠜⠼⠒⠀⠿⠭⠢⠣⠤⠭⠢⠡⠿⠘⠣⠘⠓⠀⠒⠂⠀⠿⠋⠦⠭⠢⠣⠴⠤⠋⠦⠭⠢⠡⠴⠿⠘⠣⠘⠑")?; + return Ok(()); } #[test] -fn power_ordinal_a_16_4_07() { +fn power_ordinal_a_16_4_07() -> Result<()> { let expr= r#"(p+q)ième"#; - test_braille("French", expr, "⠦⠏⠖⠟⠴⠈⠊⠮⠍"); + test_braille("French", expr, "⠦⠏⠖⠟⠴⠈⠊⠮⠍")?; + return Ok(()); } #[test] -fn power_ordinal_b_16_4_07() { +fn power_ordinal_b_16_4_07() -> Result<()> { let expr= r#"(p+q)ième"#; - test_braille("French", expr, "⠦⠏⠖⠟⠴⠈⠊⠮⠍"); + test_braille("French", expr, "⠦⠏⠖⠟⠴⠈⠊⠮⠍")?; + return Ok(()); } From 62dfe83d738ad3662f8970e54fda345eb7a75418 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 22 Jun 2026 00:03:30 -0700 Subject: [PATCH 006/120] It appears some logic was missing parens -- not sure why this always worked... --- src/xpath_functions.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xpath_functions.rs b/src/xpath_functions.rs index 8c6bfb655..7320ed81c 100644 --- a/src/xpath_functions.rs +++ b/src/xpath_functions.rs @@ -868,8 +868,8 @@ impl IsBracketed { return false; } - if !left.is_empty() && get_text_from_COE(&children[0]) != left || - !right.is_empty() && get_text_from_COE(&children[children.len()-1]) != right { + if (!left.is_empty() && get_text_from_COE(&children[0]) != left) || + (!right.is_empty() && get_text_from_COE(&children[children.len()-1]) != right) { // left or right don't match return false; } From 3c5b1018a1a87d9b1c34790bffc6933b3c27296e Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 22 Jun 2026 00:06:00 -0700 Subject: [PATCH 007/120] Lots more cleanup of tests, additions of rules. Now at 78 pass/15 fail. --- Rules/Braille/French/French_Rules.yaml | 616 ++++++++++++------------- Rules/Braille/French/definitions.yaml | 18 + Rules/Braille/French/unicode-full.yaml | 4 +- Rules/Braille/French/unicode.yaml | 50 +- Rules/prefs.yaml | 2 + src/braille.rs | 173 +++---- tests/braille/French/chemistry.rs | 4 +- tests/braille/French/french.rs | 145 ++---- 8 files changed, 462 insertions(+), 550 deletions(-) diff --git a/Rules/Braille/French/French_Rules.yaml b/Rules/Braille/French/French_Rules.yaml index 3dad1abe3..741a7d0e8 100644 --- a/Rules/Braille/French/French_Rules.yaml +++ b/Rules/Braille/French/French_Rules.yaml @@ -1,27 +1,27 @@ --- -- - name: whitespace-omission - tag: "!*" - match: "not(self::m:math) and not($MatchingWhitespace) and (@data-previous-space-width >= 0.25 or @data-following-space-width >= 0.25)" - replace: - - with: - variables: [MatchingWhitespace: "true()"] - replace: - - test: - - if: "(@class='MathML-unit' or BaseNode(.)[@class='MathML-unit']) and @data-previous-space-width >= 0.25" # BANA 5.3(a) - then: [t: "𝐖"] - - else_if: "@data-previous-space-width > 1.1" - then: [t: "⠬"] - - else_if: "@data-previous-space-width >= 0.25" # thickspace - then: [t: "W"] - - x: "." - - test: - - if: "(@class='MathML-unit' or BaseNode(.)[@class='MathML-unit']) and @data-following-space-width >= 0.25" # BANA 5.3(a) - then: [t: "𝐖"] - - else_if: "@data-following-space-width > 1.1" - then: [t: "⠬"] - - else_if: "@data-following-space-width >= 0.25" # thickspace - then: [t: "W"] +# - +# name: whitespace-omission +# tag: "!*" +# match: "not(self::m:math) and not($MatchingWhitespace) and (@data-previous-space-width >= 0.25 or @data-following-space-width >= 0.25)" +# replace: +# - with: +# variables: [MatchingWhitespace: "true()"] +# replace: +# - test: +# - if: "(@class='MathML-unit' or BaseNode(.)[@class='MathML-unit']) and @data-previous-space-width >= 0.25" # BANA 5.3(a) +# then: [t: "𝐖"] +# - else_if: "@data-previous-space-width > 1.1" +# then: [t: "⠬"] +# - else_if: "@data-previous-space-width >= 0.25" # thickspace +# then: [t: "W"] +# - x: "." +# - test: +# - if: "(@class='MathML-unit' or BaseNode(.)[@class='MathML-unit']) and @data-following-space-width >= 0.25" # BANA 5.3(a) +# then: [t: "𝐖"] +# - else_if: "@data-following-space-width > 1.1" +# then: [t: "⠬"] +# - else_if: "@data-following-space-width >= 0.25" # thickspace +# then: [t: "W"] - name: omission-intent @@ -46,9 +46,9 @@ - test: if: "NeedsToBeGrouped(*[1], 'French', true())" then: - - t: "⠰" + - t: "o" - x: "*[1]" - - t: "⠆" + - t: "c" else: - x: "*[1]" @@ -59,20 +59,20 @@ replace: - t: "⠈" - test: - if: "NeedsToBeGrouped(*[2], 'French', true())" + if: "NeedsToBeGrouped(*[2], 'French', false())" then: - - t: "⠰" + - t: "o" - x: "*[2]" - - t: "⠆" + - t: "c" else: - x: "*[2]" - t: "⠜" - test: - if: "NeedsToBeGrouped(*[1], 'French', true())" + if: "NeedsToBeGrouped(*[1], 'French', false())" then: - - t: "⠰" + - t: "o" - x: "*[1]" - - t: "⠆" + - t: "c" else: - x: "*[1]" @@ -101,45 +101,23 @@ match: "." replace: - test: - if: "DEBUG(NeedsToBeGrouped(DEBUG(*[1]), 'French', true()))" + if: "NeedsToBeGrouped(*[1], 'French', true())" then: - - t: "⠰" + - t: "o" - x: "*[1]" - - t: "⠆" + - t: "c" else: - x: "*[1]" - t: "⠌" - test: if: "NeedsToBeGrouped(*[2], 'French', true())" then: - - t: "⠰" + - t: "o" - x: "*[2]" - - t: "⠆" + - t: "c" else: - x: "*[2]" -- - #GTM 14.3.3 (not sure what else vertical juxtaposition applies to) - name: binomial-frac - tag: mrow - match: "IsBracketed(., '(', ')') and *[2][self::m:mfrac][@linethickness=0]" - replace: - - x: "*[1]" - - x: "*[2]/*[1]" - - t: "⠰⠻" - - x: "*[2]/*[2]" - - x: "*[3]" - -- name: binomial-table - tag: mrow - match: "IsBracketed(., '(', ')') and *[2][self::m:mtable][count(*)=2 and count(*[1])=1] and contains(@intent, 'binomial(')" - replace: - - x: "*[1]" - - x: "*[2]/*[1]/*[1]/*[1]" - - t: "⠰⠻" - - x: "*[2]/*[2]/*[1]/*[1]" - - x: "*[3]" - # # Matrix/Determinant rules # matrix and determinant are the same other than "matrix"/"determinant" based on the bracketing chars @@ -216,9 +194,12 @@ variables: - RowStart: "''" # empty string -- it needs to be set - RowEnd: "''" # empty string -- it needs to be set - - NewScriptContext: "''" # empty string -- it needs to be set - MatchingWhitespace: "false()" - replace: [x: "*"] + replace: + - test: + if: "@display='inline' and not(*[1][self::m:mi])" # FIX: should check to make sure it is only (Roman?) letters + then: [t: "P"] # Add inline indicator + - x: "*" - name: empty-mrow @@ -231,13 +212,70 @@ name: trig-functions tag: mrow match: "count(*)=3 and *[2]='\u2061' and - IsInDefinition(BaseNode(*[1]), 'Braille', 'TrigFunctionNames') and not(IsBracketed(*[3], '(', ')'))" + (IsInDefinition(BaseNode(*[1]), 'Braille', 'TrigFunctionNames') or BaseNode(*[1]) = 'lim') and not(IsBracketed(*[3], '(', ')'))" replace: - x: "*[1]" - - t: "⠰" - - x: "*[3]" - - t: "⠆" + - test: + # p51 -- use short form for (some) trig functions; always group unless short form and no grouping needed + if: "not($French_UseShortForm and IsInDefinition(BaseNode(*[1]), 'Braille', 'ShortForms')) or + NeedsToBeGrouped(*[3], 'French', true())" + then: + - t: "o" + - x: "*[3]" + - t: "c" + else: + - x: "*[3]" + +# - +# name: binomial-frac +# tag: mrow +# match: "IsBracketed(., '(', ')') and *[2][self::m:mfrac][@linethickness=0]" +# replace: +# - x: "*[1]" +# - x: "*[2]/*[1]" +# - t: "⠰⠻" +# - x: "*[2]/*[2]" +# - x: "*[3]" + +# - name: binomial-table +# tag: mrow +# match: "IsBracketed(., '(', ')') and *[2][self::m:mtable][count(*)=2 and count(*[1])=1] and contains(@intent, 'binomial(')" +# replace: +# - x: "*[1]" +# - x: "*[2]/*[1]/*[1]/*[1]" +# - t: "⠰⠻" +# - x: "*[2]/*[2]/*[1]/*[1]" +# - x: "*[3]" + +- + # integral rules -- need to group the integrand (but not the 'd x') + # we look for what follows the integral and check for 'd' in the next to last element and group what comes before it (assuming there is more than one element) + # note: we need to skip the invisible times when checking for 'd' + name: integrals + tag: mrow + match: "BaseNode(*[1])[translate(., '∫∬∭∮', '')=''] and + *[2][self::m:mrow][count(*)>5 and *[last()-2][self::m:mi][.='d' or .='ⅆ']]" + replace: + - x: "*[1]" + - t: "o" + - x: "*[2]/*[position() <= last()- 4]" + - t: "c" + - x: "*[2]/*[last()-2]" + - x: "*[2]/*[last()]" +- + # grouping rules -- need to add grouping symbols for some cases + name: function-call + tag: mrow + # we include invisible times in the match because some AbbreviationsForGrouping are not function names + match: "count(*)=3 and *[2][self::m:mo][.='\u2061'or .='\u2062'] and + not(*[3][self::m:mrow][IsBracketed(., '(', ')') or IsBracketed(., '[', ']')]) and + *[1][self::m:mi][IsInDefinition(., 'Braille', 'TrigFunctionNames') or IsInDefinition(., 'Braille', 'AbbreviationsForGrouping')]" + replace: + - x: "*[1]" + - t: "o" + - x: "DEBUG(*[3])" + - t: "c" - name: default @@ -269,23 +307,11 @@ - x: "text()" - - name: default - tag: mo - # add spaces around comparison operators unless they are in a script position, - variables: # ratio not spaced in UEB; also make sure it is an infix operator (e.g., "~" can be prefix) - - AddSpaces: "$UEB_UseSpacesAroundAllOperators" - match: "." - replace: - - test: - if: "$AddSpaces" - then: - - test: - if: "preceding-sibling::*" - then: [t: "W"] - - x: "text()" - - test: - if: "$AddSpaces and following-sibling::*" - then: [t: "W"] + name: default + tag: mo + match: "." + replace: + - x: "text()" - @@ -299,7 +325,10 @@ tag: [mi, mtext] match: "." replace: - - x: "BrailleChars(., 'French')" + - test: + if: "$French_UseShortForm and IsInDefinition(., 'Braille', 'ShortForms')" + then: [x: "BrailleChars(DefinitionValue(., 'Braille', 'ShortForms'), 'French')"] + else: [x: "BrailleChars(., 'French')"] - name: default @@ -333,23 +362,13 @@ - # special hack to catch double over/under bar built from nested mover/munder name: doubled-line-hack-exception - tag: [mover, munder] + tag: mover match: - "*[2][self::m:mo and string-length(.) = 1 and (.='¯')] and" # has line/bar - - "( (self::m:mover and *[1][self::m:mover]) or (self::m:munder and *[1][self::m:munder]) ) and" # both mover/munder - - "*[1]/*[2][self::m:mo and string-length(.) = 1 and (.='¯')]" # other mover/munder has line/bar + - "(*[1][self::m:mover] and *[1]/*[2][self::m:mo and string-length(.) = 1 and (.='¯')])" replace: - - test: - if: "self::m:munder" - then: [t: "⠨"] - t: "⠘⠸⠒" - - test: - if: "NeedsToBeGrouped(*[1], 'French', true())" - then: - - t: "⠰" - - x: "*[1]" - - t: "⠆" - else: [x: "*[1]"] + - x: "*[1]" - name: single-char-exceptions # GTM 12 @@ -377,9 +396,9 @@ # - test: # if: "NeedsToBeGrouped(*[1], 'French', true())" # then: - # - t: "⠰" + # - t: "o" # - x: "*[1]" - # - t: "⠆" + # - t: "c" # else: [x: "*[1]"] - x: "*[1]" @@ -390,55 +409,55 @@ # We combine them here name: single-char-exceptions tag: [msub, msup] - match: "(self::m:msub and @data-chem-formula) or (self::m:msup and *[2][translate(., \"'′″‴\",'')=''])" + match: "(@data-chem-formula) or (self::m:msup and *[2][translate(., \"'′″‴\",'')=''])" replace: + - x: "*[1]" - test: - if: "NeedsToBeGrouped(*[1], 'French', true())" + # we need to group an operator if it is followed by an operator (see p9 of chem spec) -- don't want 2 "+"s in a row + # it is a bit tricky, we used following and preceding because it might be at a different level of the tree + if: "@data-chem-formula and *[2][self::m:mo] and following::*[1][self::m:mo and .=preceding::*[1]]" then: - - t: "⠰" - - x: "*[1]" - - t: "⠆" - else: [x: "*[1]"] + - t: "o" + - x: "*[2]" + - t: "c" + else: [x: "*[2]"] + +- + name: single-char-exceptions + tag: [mover] + match: "*[2][translate(., \"⏜^→⇀¯⌣◌̌~\",'')='']" + replace: - x: "*[2]" + - x: "*[1]" - name: default tag: [msub, msup, munder, mover] match: "." - variables: [AddSpaces: "(self::m:munder or self::m:mover) and IsInDefinition(BaseNode(*[1]), 'Braille', 'NemethComparisonOperators')"] replace: - test: - if: "$AddSpaces" - then: [t: "W"] - - test: - if: "NeedsToBeGrouped(*[1], 'French', self::m:msub or self::m:msup)" + if: "(self::m:munder or self::m:mover) and NeedsToBeGrouped(*[1], 'French', true())" then: - - t: "⠰" + - t: "o" - x: "*[1]" - - t: "⠆" + - t: "c" else: [x: "*[1]"] - - with: # this could be tighter and be just around the *[2] part, but this is consistent with msubsup below - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - test: - - if: "self::m:msup" - then: [t: "⠈"] - - else_if: "self::m:msub" - then: [t: "⠢"] - - else_if: "self::m:munder" - then: [t: "⠢⠢"] - else: [t: "⠈⠈"] # mover - - test: - if: "NeedsToBeGrouped(*[2], 'French', false())" - then: - - t: "⠰" - - x: "*[2]" - - t: "⠆" - else: [x: "*[2]"] - - t: "#" # signal end script/numeric mode - test: - if: "$AddSpaces" - then: [t: "W"] + - if: "self::m:msup" + then: [t: "⠈"] + - else_if: "self::m:msub" + then: [t: "⠢"] + - else_if: "self::m:munder" + then: [t: "⠢⠢"] + else: [t: "⠈⠈"] # mover + - test: + if: "DEBUG(NeedsToBeGrouped(*[2], 'French', false()))" + then: + - t: "o" + - x: "*[2]" + - t: "c" + else: [x: "*[2]"] + - t: "#" # signal end script/numeric mode @@ -448,44 +467,35 @@ match: "count(*)=3" # catches mmultiscripts equiv variables: [IsPrimeSuperScript: "not(self::m:munderover) and *[3][translate(., \"'′″‴\",'')='']"] replace: - - test: - if: "NeedsToBeGrouped(*[1], 'French', self::m:msubsup)" - then: - - t: "⠰" - - x: "*[1]" - - t: "⠆" - else: [x: "*[1]"] + - x: "*[1]" - test: if: "$IsPrimeSuperScript" then: [x: "*[3]"] - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: + - test: + if: "self::m:munderover" + then: [t: "⠢⠢"] + else: [t: "⠢"] + - test: + if: "DEBUG(NeedsToBeGrouped(*[2], 'French', false()))" + then: + - t: "o" + - x: "*[2]" + - t: "c" + else: [x: "*[2]"] + - test: + if: "not($IsPrimeSuperScript)" + then: - test: if: "self::m:munderover" - then: [t: "⠢⠢"] - else: [t: "⠢"] - - test: - if: "NeedsToBeGrouped(*[2], 'French', false())" - then: - - t: "⠰" - - x: "*[2]" - - t: "⠆" - else: [x: "*[2]"] + then: [t: "⠈⠈"] + else: [t: "⠈"] - test: - if: "not($IsPrimeSuperScript)" + if: "NeedsToBeGrouped(*[3], 'French', false())" then: - - test: - if: "self::m:munderover" - then: [t: "⠈⠈"] - else: [t: "⠈"] - - test: - if: "NeedsToBeGrouped(*[3], 'French', false())" - then: - - t: "⠰" - - x: "*[3]" - - t: "⠆" - else: [x: "*[3]"] + - t: "o" + - x: "*[3]" + - t: "c" + else: [x: "*[3]"] - @@ -498,31 +508,25 @@ - test: if: "not(*[3][self::m:none])" then: - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - t: "⠠⠢" - - test: - if: "NeedsToBeGrouped(*[3], 'French', true())" - then: - - t: "⠰" - - x: "*[3]" - - t: "⠆" - else: [x: "*[3]"] + - t: "⠠⠢" + - test: + if: "NeedsToBeGrouped(*[3], 'French', true())" + then: + - t: "o" + - x: "*[3]" + - t: "c" + else: [x: "*[3]"] - test: if: "not(*[4][self::m:none])" then: - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - t: "⠠⠈" - - test: - if: "NeedsToBeGrouped(*[4], 'French', false())" - then: - - t: "⠰" - - x: "*[4]" - - t: "⠆" - else: [x: "*[4]"] + - t: "⠠⠈" + - test: + if: "NeedsToBeGrouped(*[4], 'French', false())" + then: + - t: "o" + - x: "*[4]" + - t: "c" + else: [x: "*[4]"] - name: default @@ -540,62 +544,50 @@ - test: if: "not($Prescripts[1][self::m:none])" then: - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - t: "⠢" - - test: - if: "NeedsToBeGrouped($Prescripts[1], 'French', true())" - then: - - t: "⠰" - - x: "$Prescripts[1]" - - t: "⠆" - else: [x: "$Prescripts[1]"] + - t: "⠢" + - test: + if: "NeedsToBeGrouped($Prescripts[1], 'French', true())" + then: + - t: "o" + - x: "$Prescripts[1]" + - t: "c" + else: [x: "$Prescripts[1]"] - test: if: "not($Prescripts[2][self::m:none])" then: - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - t: "⠈" - - test: - if: "NeedsToBeGrouped($Prescripts[2], 'French', false())" - then: - - t: "⠰" - - x: "$Prescripts[2]" - - t: "⠆" - else: [x: "$Prescripts[2]"] + - t: "⠈" + - test: + if: "NeedsToBeGrouped($Prescripts[2], 'French', false())" + then: + - t: "o" + - x: "$Prescripts[2]" + - t: "c" + else: [x: "$Prescripts[2]"] - test: if: "count($Prescripts)>2" then: - test: if: "not($Prescripts[3][self::m:none])" then: - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - t: "⠢" - - test: - if: "NeedsToBeGrouped($Prescripts[3], 'French', false())" - then: - - t: "⠰" - - x: "$Prescripts[3]" - - t: "⠆" - else: [x: "$Prescripts[3]"] + - t: "⠢" + - test: + if: "NeedsToBeGrouped($Prescripts[3], 'French', false())" + then: + - t: "o" + - x: "$Prescripts[3]" + - t: "c" + else: [x: "$Prescripts[3]"] - test: if: "not($Prescripts[4][self::m:none])" then: - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - t: "⠈" - - test: - if: "NeedsToBeGrouped($Prescripts[4], 'French', false())" - then: - - t: "⠰" - - x: "$Prescripts[4]" - - t: "⠆" - else: [x: "$Prescripts[4]"] + - t: "⠈" + - test: + if: "NeedsToBeGrouped($Prescripts[4], 'French', false())" + then: + - t: "o" + - x: "$Prescripts[4]" + - t: "c" + else: [x: "$Prescripts[4]"] - test: if: "count($Prescripts) > 4" # give up and just dump them out so at least the content is there then: [x: "$Prescripts[position() > 4]"] @@ -607,124 +599,100 @@ - test: if: "not($Postscripts[1][self::m:none])" then: - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - t: "⠢" - - test: - if: "NeedsToBeGrouped($Postscripts[1], 'French', false())" - then: - - t: "⠰" - - x: "$Postscripts[1]" - - t: "⠆" - else: [x: "$Postscripts[1]"] + - t: "⠢" + - test: + if: "NeedsToBeGrouped($Postscripts[1], 'French', false())" + then: + - t: "o" + - x: "$Postscripts[1]" + - t: "c" + else: [x: "$Postscripts[1]"] - test: if: "not($Postscripts[2][self::m:none])" then: - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - t: "⠈" - - test: - if: "NeedsToBeGrouped($Postscripts[2], 'French', false())" - then: - - t: "⠰" - - x: "$Postscripts[2]" - - t: "⠆" - else: [x: "$Postscripts[2]"] + - t: "⠈" + - test: + if: "NeedsToBeGrouped($Postscripts[2], 'French', false())" + then: + - t: "o" + - x: "$Postscripts[2]" + - t: "c" + else: [x: "$Postscripts[2]"] - test: if: "count($Postscripts)>2" then: - test: if: "not($Postscripts[3][self::m:none])" then: - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - t: "⠢" - - test: - if: "NeedsToBeGrouped($Postscripts[3], 'French', false())" - then: - - t: "⠰" - - x: "$Postscripts[3]" - - t: "⠆" - else: [x: "$Postscripts[3]"] + - t: "⠢" + - test: + if: "NeedsToBeGrouped($Postscripts[3], 'French', false())" + then: + - t: "o" + - x: "$Postscripts[3]" + - t: "c" + else: [x: "$Postscripts[3]"] - test: if: "not($Postscripts[4][self::m:none])" then: - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - t: "⠈" - - test: - if: "NeedsToBeGrouped($Postscripts[4], 'French', false())" - then: - - t: "⠰" - - x: "$Postscripts[4]" - - t: "⠆" - else: [x: "$Postscripts[4]"] + - t: "⠈" + - test: + if: "NeedsToBeGrouped($Postscripts[4], 'French', false())" + then: + - t: "o" + - x: "$Postscripts[4]" + - t: "c" + else: [x: "$Postscripts[4]"] - test: if: "count($Postscripts)>4" then: - test: if: "not($Postscripts[5][self::m:none])" then: - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - t: "⠢" - - test: - if: "NeedsToBeGrouped($Postscripts[5], 'French', false())" - then: - - t: "⠰" - - x: "$Postscripts[5]" - - t: "⠆" - else: [x: "$Postscripts[5]"] + - t: "⠢" + - test: + if: "NeedsToBeGrouped($Postscripts[5], 'French', false())" + then: + - t: "o" + - x: "$Postscripts[5]" + - t: "c" + else: [x: "$Postscripts[5]"] - test: if: "not($Postscripts[6][self::m:none])" then: - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - t: "⠈" - - test: - if: "NeedsToBeGrouped($Postscripts[6], 'French', false())" - then: - - t: "⠰" - - x: "$Postscripts[6]" - - t: "⠆" - else: [x: "$Postscripts[6]"] + - t: "⠈" + - test: + if: "NeedsToBeGrouped($Postscripts[6], 'French', false())" + then: + - t: "o" + - x: "$Postscripts[6]" + - t: "c" + else: [x: "$Postscripts[6]"] - test: if: "count($Postscripts)>6" then: - test: if: "not($Postscripts[7][self::m:none])" then: - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - t: "⠢" - - test: - if: "NeedsToBeGrouped($Postscripts[7], 'French', false())" - then: - - t: "⠰" - - x: "$Postscripts[7]" - - t: "⠆" - else: [x: "$Postscripts[7]"] + - t: "⠢" + - test: + if: "NeedsToBeGrouped($Postscripts[7], 'French', false())" + then: + - t: "o" + - x: "$Postscripts[7]" + - t: "c" + else: [x: "$Postscripts[7]"] - test: if: "not($Postscripts[8][self::m:none])" then: - - with: - variables: [NewScriptContext: "in"] # value doesn't matter -- just can't be empty string - replace: - - t: "⠈" - - test: - if: "NeedsToBeGrouped($Postscripts[8], 'French', false())" - then: - - t: "⠰" - - x: "$Postscripts[8]" - - t: "⠆" - else: [x: "$Postscripts[8]"] + - t: "⠈" + - test: + if: "NeedsToBeGrouped($Postscripts[8], 'French', false())" + then: + - t: "o" + - x: "$Postscripts[8]" + - t: "c" + else: [x: "$Postscripts[8]"] - test: if: "count($Postscripts) > 8" # give up and just dump them out so at least the content is there then: @@ -754,7 +722,6 @@ tag: menclose match: "." # FIX: can't find a rule that says anything about comparison operator spacing and enclosure - variables: [AddSpaces: "parent::*[self::m:mrow] and *[1][ self::m:mo and IsInDefinition(., 'Braille', 'NemethComparisonOperators')]"] replace: - test: if: "contains(concat(' ', normalize-space(@notation), ' '), ' left ')" #avoid 'leftarrow' @@ -762,16 +729,10 @@ - test: if: "contains(@notation,'box')" # box and roundedbox then: - # - test: - # if: "$AddSpaces" - # then: [t: " "] - t: "1⠫⠼⠙" # square (no rectangle in UEB) - test: if: "contains(@notation,'circle')" then: - # - test: - # if: "$AddSpaces" - # then: [t: " "] - t: "1⠫⠿" # circle (no oval in UEB) # ??? What should happen with arrow? # If there is a box/circle with arrows only and an empty child, @@ -825,9 +786,9 @@ - test: if: "NeedsToBeGrouped(*[1], 'French', false())" then: - - t: "⠰" + - t: "o" - x: "*[1]" - - t: "⠆" + - t: "c" else: [x: "*[1]"] # - test: @@ -874,13 +835,10 @@ - t: "1⠈⠱" # cancellation - test: if: - - "not($AddSpaces) and contains(@notation,'box') or contains(@notation,'circle') or" + - "contains(@notation,'box') or contains(@notation,'circle') or" - "contains(@notation,'arrow') or contains(@notation,'phasorangle')" then: - t: "⠻" # terminate shape - # - test: - # if: "$AddSpaces" - # then: [t: " "] - test: if: "contains(concat(' ', normalize-space(@notation), ' '), ' right ')" #avoid 'rightarrow' then: [t: "⠸"] diff --git a/Rules/Braille/French/definitions.yaml b/Rules/Braille/French/definitions.yaml index 4d0c77efb..332abd498 100644 --- a/Rules/Braille/French/definitions.yaml +++ b/Rules/Braille/French/definitions.yaml @@ -1,2 +1,20 @@ --- - include: "../definitions.yaml" + +- AbbreviationsForGrouping: { + "arg", "Card", "rot", + "arcsin", "arccos", "arctan", "arccotan", + "ch", "sh", "th", + } + +- ShortForms: { + # p51 -- short forms for trig functions + # This uses a hack of private space chars that are then mapped to braille chars via unicode.yaml. + # We need to do this because otherwise it tries to translate the short form. + # ⠬ -> \ue000 + # ⠨ -> \ue001 + # ⠮ -> \ue002 + # ⠘ -> \ue003 + "sin": "\ue000", cos: "\ue001\ue000", tan: "\ue002", "cotan": "\ue001\ue002", + arcsin: "\ue003\ue000", arccos: "\ue003\ue001\ue002", arctan: "\ue003\ue002", arccotan: "\ue003\ue001\ue002", + } \ No newline at end of file diff --git a/Rules/Braille/French/unicode-full.yaml b/Rules/Braille/French/unicode-full.yaml index 91054d0cb..2f6ecc9b0 100644 --- a/Rules/Braille/French/unicode-full.yaml +++ b/Rules/Braille/French/unicode-full.yaml @@ -1,7 +1,7 @@ --- - - "⇌": [t: "⠸⠻"] # 0x21CC (Rightwards harpoon over leftwards harpoon (equilibrium)) -# - "⥂": [t: ""] # 0x2942 (Rightwards arrow above short leftwards arrow (equilibrium, trend to the right)) + - "⇌": [t: "⠸⠻"] # 0x21CC (Rightwards harpoon over leftwards harpoon (equilibrium)) +# - "⥂": [t: ""] # 0x2942 (Rightwards arrow above short leftwards arrow (equilibrium, trend to the right)) # - "⥄": [t: ""] # 0x2944 (Short rightwards arrow above leftwards arrow (equilibrium, trend to the left)) # Mappings for some new Unicode chemistry arrows/symbols diff --git a/Rules/Braille/French/unicode.yaml b/Rules/Braille/French/unicode.yaml index e37c31f40..0105b376e 100644 --- a/Rules/Braille/French/unicode.yaml +++ b/Rules/Braille/French/unicode.yaml @@ -42,7 +42,22 @@ - "w": [t: "L⠺"] # 0x77 - "x": [t: "L⠭"] # 0x78 - "y": [t: "L⠽"] # 0x79 - - "z": [t: "L⠵"] # 0x7a + - "z": [t: "L⠵"] # 0x7a + + - "ç": [t: "L⠯"] # 0x282f + - "é": [t: "L⠿"] # 0x283f + - "à": [t: "L⠷"] # 0x2837 + - "è": [t: "L⠮"] # 0x282e + - "ù": [t: "L⠾"] # 0x283e + - "â": [t: "L⠡"] # 0x2821 + - "ê": [t: "L⠣"] # 0x2823 + - "î": [t: "L⠩"] # 0x2829 + - "ô": [t: "L⠹"] # 0x2839 + - "û": [t: "L⠱"] # 0x2831 + - "ë": [t: "L⠫"] # 0x282b + - "ï": [t: "L⠻"] # 0x283b + - "ü": [t: "L⠳"] # 0x2833 + - "œ": [t: "L⠪"] # 0x282a - "A": [t: "CL⠁"] # 0x41 - "B": # 0x42 @@ -190,6 +205,7 @@ - "%": [t: "⠐⠬"] # 0x0025 (percent sign) - "(": [t: "⠦"] # 0x0028 (left parenthesis) - ")": [t: "⠴"] # 0x0029 (right parenthesis) + - "*": [t: "⠐⠔"] # 0x002A (asterisk) - "+": [t: "⠖"] # 0x002B (regular plus) - ",": [t: "⠂"] # 0x002C (comma) - "-": [t: "⠤"] # 0x002D (regular minus) @@ -206,11 +222,13 @@ - "8": [t: "N⠳"] # 0x0038 (digit eight) - "9": [t: "N⠪"] # 0x0039 (digit nine) - ":": [t: "⠒"] # 0x003A (colon) + - ";": [t: "⠆"] # 0x003B (semicolon) - "<": [t: "⠐⠣"] # 0x003C (less than sign) - "=": [t: "⠶"] # 0x003D (regular equals sign) - ">": [t: "⠐⠜"] # 0x003E (greater than sign ( regular )) - "[": [t: "⠷"] # 0x005B (left square bracket) - "]": [t: "⠾"] # 0x005D (right square bracket) + - "^": [t: "⠘⠒"] # 0x005E (caret) - "{": # 0x007B ( left curly brace) - test: if: "following-sibling::*[1][self::m:mtable]" @@ -221,8 +239,9 @@ else: [t: "⠨⠦"] # regular brace - "|": [t: "⠿"] # 0x007C (vertical bar) - "}": [t: "⠨⠴"] # 0x007D (right curly brace) - - "~": [t: "⠘⠶"] # 0x007E (simple tilde (is related to, is similar)) + - "~": [t: "⠐⠸⠒"] # 0x007E (simple tilde (is related to, is similar)) - "¢": [t: "⠉"] # 0x00A2 (cent) + - "¯": [t: "⠸⠒"] # 0x00AF (bar) - "°": [t: "⠐⠕"] # 0x00B0 (degree sign) - "±": [t: "⠖⠤"] # 0x00B1 (plus or minus) - "¼": [t: "⠡⠌⠹"] # 0x00BC (vulgar fraction one quarter) @@ -233,6 +252,7 @@ - "ב": [t: "⠘⠘⠃"] # 0x05D1 ( beth) - "ג": [t: "⠘⠘⠛"] # 0x05D2 (gimel) - "ד": [t: "⠘⠘⠙"] # 0x05D3 (daleth) + - "◌̌": [t: "⠸⠘⠒"] # 0x030C (combing caron) - "‖": [t: "⠘⠿"] # 0x2016 (double vertical bar) - "‰": [t: "⠐⠬⠬"] # 0x2030 (per mille sign) - "′": [t: "⠄"] # 0x2032 (prime) @@ -243,7 +263,17 @@ - "ℵ": [t: "⠘⠘⠁"] # 0x2135 (alef) - "←": [t: "⠸⠪"] # 0x2190 (left pointing arrow) - "↑": [t: "⠘⠻"] # 0x2191 (arrow pointing up) - - "→": [t: "⠸⠱"] # 0x2192 (left pointing arrow) + - "→": # 0x2192 (right pointing arrow) + - test: + if: "parent::m:mover and not(following-sibling::*)" + then: [t: "⠨⠒"] + else: [t: "⠸⠱"] + - "⇀": # 0x2192 (right pointing arrow) + - test: + if: "parent::m:mover and not(following-sibling::*)" + then: [t: "⠨⠒"] + else: [t: "⠸⠱"] + - "↓": [t: "⠨⠻"] # 0x2193 (arrow pointing down) - "↔": [t: "⠐⠻"] # 0x2194 (horizontal two-way arrow) - "↖": [t: "⠘⠪"] # 0x2196 (north west arrow) @@ -275,7 +305,9 @@ - "∓": [t: "⠤⠖"] # 0x2213 (minus or plus) - "∘": [t: "⠸⠼"] # 0x2218 (hollow dot) - "∞": [t: "⠘⠉"] # 0x221E (infinity) - - "∥": [t: "⠸⠳"] # 0x2225 (is parallel to) + - "⋯": [t: "⠲⠲⠲"] # 0x22EF (horizontal ellipsis) + - "∥": [t: "⠘⠿"] # 0x2225 (is parallel to, but French braille uses this for magnitude, etc) + - "⫽": [t: "⠸⠳"] # 0x2A7D (DOUBLE SOLIDUS OPERATOR, but French braille uses this for parallel to sign) - "∧": [t: "⠘⠔"] # 0x2227 (logical product (and, meet)) - "∨": [t: "⠘⠢"] # 0x2228 (logical sum (join,or)) - "∩": [t: "⠘⠖"] # 0x2229 (intersection sign ( cap )) @@ -308,10 +340,18 @@ - "⊕": [t: "⠨⠖"] # 0x2295 (circle with interior plus sign (exclusive or, direct sum, diletion)) - "⊗": [t: "⠨⠔"] # 0x2297 (circle with interior cross (tensor product)) - "⊥": [t: "⠘⠳"] # 0x22A5 (is perpendicular to) - - "⋅": [t: "⠔⠔"] # 0x22C5 (dot,and times) + - "⋅": [t: "⠔⠔"] # 0x22C5 (dot,and times) - "⌄": [t: "⠸⠘⠒"] # 0x2304 (inverted caret) + - "⌣": [t: "⠈⠈⠈"] # 0x2323 (smile) - "⏜": [t: "⠈⠒"] # 0x23DC (arc concave upward) - "△": [t: "⠨⠘⠙"] # 0x25B3 (increment (laplacian Delta) ) - "✕": [t: "⠐⠐⠔"] # 0x2715 (multiplication X) - "⟧": [t: "⠨⠾"] # 0x27E7 (right barred bracket) - "⪥": [t: "⠐⠜⠐⠣"] # 0x2AA5 (greater than sign followed by less than sign) + - "⫽": [t: "⠸⠳"] # 0x2A7D (DOUBLE SOLIDUS OPERATOR) + +# Private space chars used for short forms for trig functions + - "\ue000": [t: "L⠬"] + - "\ue001": [t: "L⠨"] + - "\ue002": [t: "L⠮"] + - "\ue003": [t: "L⠘"] \ No newline at end of file diff --git a/Rules/prefs.yaml b/Rules/prefs.yaml index 38fc22efc..395583be8 100644 --- a/Rules/prefs.yaml +++ b/Rules/prefs.yaml @@ -95,6 +95,8 @@ LaTeX: UseShortName: false # Use the short form for the latex (e.g., "~a" instead of "\alpha") + French: + UseShortForm: false # Use the short form for trig functions and other abbreviations Other: # Note: DecimalSeparator is user-facing (can be Auto), DecimalSeparators is code-facing (always a char) diff --git a/src/braille.rs b/src/braille.rs index a4d6395e8..73cb6c5c8 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -2278,84 +2278,53 @@ fn swedish_cleanup(pref_manager: Ref, raw_braille: String) -> } static FRENCH_INDICATOR_REPLACEMENTS: phf::Map<&str, &str> = phf_map! { - // FIX: this needs to be fully checked against the French spec - // FIX: this needs cleaning up -- not all of these are used + // Keys match indicator chars emitted by French unicode.yaml, unicode-full.yaml, and French_Rules.yaml "S" => "XXX", // sans-serif -- from prefs "B" => "⠸", // bold - "𝔹" => "XXX", // blackboard -- from prefs + "𝔹" => "⠨", // blackboard -- from prefs "T" => "⠈", // script "I" => "⠸", // italic - "R" => "", // roman - "E" => "⠰", // English "1" => "⠀", // Grade 1 symbol (used for number followed by a letter) "L" => "", // Letter left in to assist in locating letters "D" => "XXX", // German (Deutsche) -- from prefs "G" => "⠘", // Greek - "V" => "XXX", // Greek Variants - // "H" => "⠠⠠", // Hebrew - // "U" => "⠈⠈", // Russian "C" => "⠨", // capital - "𝑐" => "", // second or latter braille cell of a capital letter "𝐶" => "⠨", // capital that never should get whitespace in front (from chemical element) "N" => "", // number indicator - "n" => "", // number indicator for drop numbers (special case with close parens) - "t" => "⠱", // shape terminator "W" => "⠀", // whitespace" - "𝐖"=> "⠀", // whitespace "s" => "⠆", // typeface single char indicator - "w" => "", // typeface word indicator - "e" => "", // typeface & capital terminator - "," => "⠂", // comma - "." => "⠲", // period - "-" => "-", // hyphen - "—" => "⠠⠤", // normal dash (2014) -- assume all normal dashes are unified here [RUEB appendix 3] - "―" => "⠐⠠⠤", // long dash (2015) -- assume all long dashes are unified here [RUEB appendix 3] - "(" => "⠦", // Not really needed, but done for consistancy with ")" - ")" => "⠴", // Needed for rules with drop numbers to avoid mistaking for dropped 0 - "↑" => "⠬", // superscript - "↓" => "⠡", // subscript + "o" => "⠰", // Opening group indicator + "c" => "⠆", // Closing group indicator + "P" => "⠠⠄", // Math modifier and code change indicator "#" => "", // signals end of script - "Z" => "⠐", // signals end of index of root, integrand/lim from function ("zone change") - }; -fn french_cleanup(pref_manager: Ref, raw_braille: String) -> String { +fn french_cleanup(_pref_manager: Ref, raw_braille: String) -> String { // FIX: need to implement this -- this is just a copy of the Vietnam code - // Empty bases are ok if they follow whitespace - static EMPTY_BASE: LazyLock = LazyLock::new(|| Regex::new(r"(^|[W𝐖w])E").unwrap()); + static REPLACE_INDICATORS: LazyLock = LazyLock::new(|| Regex::new(r"([1SB𝔹TILDGC𝐶NWsc#oc])").unwrap()); debug!("french_cleanup: start={}", raw_braille); // let result = typeface_to_word_mode(&raw_braille); // let result = capitals_to_word_mode(&result); - let result = raw_braille.replace("CG", "⠘") - .replace("𝔹C", "⠩") - .replace("DC", "⠰"); - - debug!(" after typeface/caps={}", &result); - - // these typeforms need to get pulled from user-prefs as they are transcriber-defined - let double_struck = pref_manager.pref_to_string("Vietnam_DoubleStruck"); - let sans_serif = pref_manager.pref_to_string("Vietnam_SansSerif"); - let fraktur = pref_manager.pref_to_string("Vietnam_Fraktur"); - let greek_variant = pref_manager.pref_to_string("Vietnam_GreekVariant"); + // check to see if math modifier and code change indicator are needed at start (p8) + let result = if raw_braille.starts_with("P") { + let prefix = if raw_braille.contains("o") { "⠠⠄" } else { "⠠" }; + let mut new_string = String::with_capacity(raw_braille.len() - 1 + prefix.len()); + new_string.push_str(prefix); + new_string.push_str(&raw_braille[1..]); + new_string + } else { + raw_braille + }; // This reuses the code just for getting rid of unnecessary "L"s and "N"s let result = remove_unneeded_mode_changes(&result, UEB_Mode::Grade1, UEB_Duration::Passage); debug!(" after removing mode changes={}", &result); - - let result = EMPTY_BASE.replace_all(&result, "$1"); let result: Cow<'_, str> = REPLACE_INDICATORS.replace_all(&result, |cap: &Captures| { - let matched_char = &cap[0]; - match matched_char { - "𝔹" => &double_struck, - "S" => &sans_serif, - "D" => &fraktur, - "V" => &greek_variant, - _ => match FRENCH_INDICATOR_REPLACEMENTS.get(matched_char) { - None => {error!("REPLACE_INDICATORS and FRENCH_INDICATOR_REPLACEMENTS are not in sync: missing '{}'", matched_char); ""}, + match FRENCH_INDICATOR_REPLACEMENTS.get(&cap[0]) { + None => {error!("REPLACE_INDICATORS and FRENCH_INDICATOR_REPLACEMENTS are not in sync: missing '{}'", &cap[0]); ""}, Some(&ch) => ch, - }, } }); @@ -3054,80 +3023,56 @@ impl NeedsToBeGrouped { } } - // ordinals often have an irregular start (e.g., "half") before becoming regular. - // if the number is irregular, return the ordinal form, otherwise return 'None'. - /// FIX: what needs to be implemented? - fn needs_grouping_for_french(mathml: Element, is_base: bool) -> bool { - use crate::xpath_functions::IsInDefinition; - let mut node_name = name(mathml); - if mathml.attribute_value("data-roman-numeral").is_some() { - node_name = "mi"; // roman numerals don't follow number rules - } + fn needs_grouping_for_french(mathml: Element, _is_base: bool) -> bool { + // French rules are from p26: Blocks (also mentioned elsewhere in the spec) + // if we have a "- xxx", we treat the MathML as if it were just "xxx". + // apparently, based on function_partial_derivative_14_1_04, the same goes for '∂' -- generalizing to all prefix ops... + // use crate::xpath_functions::IsInDefinition; + let mut children = mathml.children(); + let mathml = if name(mathml) == "mrow" && children.len() == 2 && + name(as_element(children[0])) == "mo" { // assuming it is a prefix op since it starts an mrow + let operand_child = as_element(children[1]); + children = operand_child.children(); + operand_child + } else { + mathml + }; + let node_name = name(mathml); + // let parent = get_parent(mathml); // there is always a "math" node because we never ask if its children need grouping + // let parent_name = name(parent); - // FIX: the leaf rules are from UEB -- check the French rules + debug!("needs_grouping_for_french: mathml (sans '-') = {}", mml_to_string(mathml)); match node_name { - "mn" => { - if !is_base { - return false; - } // clause 1 - // two 'mn's can be adjacent, in which case we need to group the 'mn' to make it clear it is separate (see bug #204) - let parent = get_parent(mathml); // there is always a "math" node - let grandparent = if name(parent) == "math" {parent} else {get_parent(parent)}; - if name(grandparent) != "mrow" { - return false; - } - let preceding = parent.preceding_siblings(); - if preceding.len() < 2 { - return false; - } - // any 'mn' would be separated from this node by invisible times - let previous_child = as_element(preceding[preceding.len()-1]); - if name(previous_child) == "mo" && as_text(previous_child) == "\u{2062}" { - let previous_child = as_element(preceding[preceding.len()-2]); - return name(previous_child) == "mn" - } else { - return false; - } + "mn" | "mi" | "mtext" => { + return as_text(mathml).contains(char::is_whitespace); }, - "mo" => { - return !is_base; // if in script, then what follows is assumed to be an operand in braille, but it isn't, so add grouping - }, - "mi" | "mtext" => { - let text = as_text(mathml); - let parent = get_parent(mathml); // there is always a "math" node - let parent_name = name(parent); - return text.chars().count() > 1 && - parent_name != "mrow" && - !IsInDefinition::is_defined_in(text, &SPEECH_DEFINITIONS, "FunctionNames").unwrap(); + "mo" => { return as_text(mathml).chars().nth(1).is_some(); }, // guessing that single chars operator don't need grouping based on set_not_belong_tor_6_4_04 + "ms" => { + return false; // FIX: is this true? Do quotes group the contents? }, "mrow" => { - // check for bracketed exprs - if IsBracketed::is_bracketed(mathml, "", "", false, true) { + if IsBracketed::is_bracketed(mathml, "", "", false, false) { return false; } - - // check for prefix at start - // example 7.12 has "2-" in superscript and is grouped, so we don't consider postfix ops - let children = mathml.children(); - if children.len() == 2 && - (name( as_element(children[0])) == "mo") { + // look for "a", "a b", etc -- last example on p26 shows "b d" doesn't need grouping + // also apparently brackets are also acceptable (see last example on p43 where numerator isn't grouped) + if children.iter().enumerate().all(|(i, &child)| { + let child = as_element(child); + if i % 2 == 1 { + name(child) == "mo" && as_text(child) == "\u{2062}" // invisible times + } else { + name(child) == "mi" || (name(child) == "mrow" && IsBracketed::is_bracketed(child, "", "", false, false)) + } + }) { return false; } - - if children.len() == 3 { - let child0 = as_element(children[0]); - let operator = as_element(children[1]); // invisible function op? - // debug!("needs_grouping_for_french: child0 ="); - if name(operator) == "mo" && as_text(operator) == "\u{2061}" && - (name(child0) == "mi" || name(child0) == "mtext") && - IsInDefinition::is_defined_in(as_text(child0), &SPEECH_DEFINITIONS, "TrigFunctionNames").unwrap() { - return false; - } - } - return true; + return !( + children.len() == 3 && + name(as_element(children[0])) == "mi" && // maybe don't need this check? E.g, sin^2 x. + name(as_element(children[1])) == "mo" && as_text(as_element(children[1])) == "\u{2061}" + ); }, - "msub" | "msup" | "msubsup" => return true, - _ => return false, + _ => return true, } } diff --git a/tests/braille/French/chemistry.rs b/tests/braille/French/chemistry.rs index b5d4606a4..4eedcda3a 100644 --- a/tests/braille/French/chemistry.rs +++ b/tests/braille/French/chemistry.rs @@ -37,7 +37,7 @@ fn equilibrium_III_5_2() -> Result<()> { let expr= r#"2H2O ⇌(2)(1)H3O++OH-"#; - test_braille("French", expr, " ⠣⠨⠓⠣⠨⠕⠰⠸⠻⠆⠢⠢⠦⠣⠴⠈⠈⠦⠡⠴⠨⠓⠩⠨⠕⠈⠰⠖⠆⠖⠨⠕⠨⠓⠈⠰⠤⠆")?; + test_braille("French", expr, "⠣⠨⠓⠣⠨⠕⠰⠸⠻⠆⠢⠢⠦⠣⠴⠈⠈⠦⠡⠴⠨⠓⠩⠨⠕⠈⠰⠖⠆⠖⠨⠕⠨⠓⠈⠰⠤⠆")?; return Ok(()); } @@ -49,6 +49,6 @@ fn equilibrium_III_5_3() -> Result<()> { R-CO-O- R'+ H2O"#; - test_braille("French", expr, " ⠨⠗⠤⠨⠉⠨⠕⠤⠨⠕⠤⠨⠓⠖⠨⠗⠄⠤⠨⠕⠨⠓ ⠸⠻⠨⠢⠰⠓⠽⠙⠗⠕⠇⠽⠎⠑⠆⠨⠔⠈⠰⠑⠎⠞⠗⠊⠋⠊⠉⠁⠞⠊⠕⠝⠆⠨⠗⠤⠨⠉⠨⠕⠤⠨⠕⠤⠨⠗⠄⠖⠨⠓⠢⠣⠨⠕")?; + test_braille("French", expr, "⠨⠗⠤⠨⠉⠨⠕⠤⠨⠕⠤⠨⠓⠖⠨⠗⠄⠤⠨⠕⠨⠓ ⠸⠻⠨⠢⠰⠓⠽⠙⠗⠕⠇⠽⠎⠑⠆⠨⠔⠈⠰⠑⠎⠞⠗⠊⠋⠊⠉⠁⠞⠊⠕⠝⠆⠨⠗⠤⠨⠉⠨⠕⠤⠨⠕⠤⠨⠗⠄⠖⠨⠓⠢⠣⠨⠕")?; return Ok(()); } diff --git a/tests/braille/French/french.rs b/tests/braille/French/french.rs index 0acd5bf56..d0c073128 100644 --- a/tests/braille/French/french.rs +++ b/tests/braille/French/french.rs @@ -57,7 +57,7 @@ fn set_included_or_equal_6_4_07() -> Result<()> { #[test] fn set_intersection_6_4_08() -> Result<()> { - let expr= r#"A∩B=⌀"#; + let expr= r#"A∩B=∅"#; test_braille("French", expr, "⠨⠁⠘⠖⠨⠃⠶⠘⠼")?; return Ok(()); } @@ -85,63 +85,63 @@ fn devided_by_7_02() -> Result<()> { #[test] fn fraction_02_8_01() -> Result<()> { - let expr= r#"a+ba"#; + let expr= r#"a+ba"#; test_braille("French", expr, "⠠⠄⠰⠁⠖⠃⠆⠌⠁")?; return Ok(()); } #[test] fn fraction_03_8_02() -> Result<()> { - let expr= r#"a+bc+d"#; + let expr= r#"a+bc+d"#; test_braille("French", expr, "⠠⠄⠰⠁⠖⠃⠆⠌⠰⠉⠖⠙⠆")?; return Ok(()); } #[test] fn fraction_04_8_03() -> Result<()> { - let expr= r#"ab+cd=ad+bcbd"#; + let expr= r#"ab+cd=ad+bcbd"#; test_braille("French", expr, "⠠⠄⠁⠌⠃⠖⠉⠌⠙⠶⠰⠁⠙⠖⠃⠉⠆⠌⠃⠙")?; return Ok(()); } #[test] fn cardinal_8_04() -> Result<()> { - let expr= r#"CardE=5"#; + let expr= r#"CardE=5"#; test_braille("French", expr, "⠠⠄⠨⠉⠁⠗⠙⠰⠨⠑⠆⠶⠱")?; return Ok(()); } #[test] fn exponential_01_8_05() -> Result<()> { - let expr= r#"ex+3"#; + let expr= r#"ex+3"#; test_braille("French", expr, "⠠⠄⠑⠈⠰⠭⠖⠩⠆")?; return Ok(()); } #[test] fn exponential_02_8_06() -> Result<()> { - let expr= r#"e7"#; + let expr= r#"e7"#; test_braille("French", expr, "⠠⠑⠈⠻")?; return Ok(()); } #[test] fn exponential_03_8_07() -> Result<()> { - let expr= r#"e−x"#; + let expr= r#"e−x"#; test_braille("French", expr, "⠠⠑⠈⠤⠭")?; return Ok(()); } #[test] fn exponential_04_8_08() -> Result<()> { - let expr= r#"e−π"#; + let expr= r#"e−π"#; test_braille("French", expr, "⠠⠑⠈⠤⠘⠏")?; return Ok(()); } #[test] fn power_negativ_one_8_09() -> Result<()> { - let expr= r#"x−1=1x"#; + let expr= r#"x−1=1x"#; test_braille("French", expr, "⠠⠭⠈⠤⠡⠶⠡⠌⠭")?; return Ok(()); } @@ -239,7 +239,7 @@ fn sub_a_prime_0_9_4_01() -> Result<()> { #[test] fn sub_mixt_0_9_4_02() -> Result<()> { - let expr= r#"x0′2"#; + let expr= r#"x′02"#; test_braille("French", expr, "⠭⠄⠢⠼⠈⠣")?; return Ok(()); } @@ -281,7 +281,7 @@ fn lef_sub_9_5_02() -> Result<()> { #[test] fn left_right_sup_9_5_03() -> Result<()> { - let expr= r#"tAtB"#; + let expr= r#"AtBt"#; test_braille("French", expr, "⠰⠈⠞⠨⠁⠆⠰⠈⠞⠨⠃⠆")?; return Ok(()); } @@ -295,21 +295,7 @@ fn left_sub_sup_test_9_5_04() -> Result<()> { #[test] fn bigsum_one_line_9_6_01() -> Result<()> { - let expr= r#"∑i=1nui=u1+u2+⋯+un"#; - test_braille("French", expr, "⠨⠘⠎⠢⠢⠰⠊⠶⠡⠆⠈⠈⠝⠥⠢⠊⠶⠥⠢⠡⠖⠥⠢⠣⠖⠲⠲⠲⠖⠥⠢⠝")?; - return Ok(()); -} -#[test] -fn bigsum_two_lines_9_6_01() -> Result<()> { - let expr= r#"∑i=1nui=u1+u2+⋯+un"#; - test_braille("French", expr, " -⠨⠘⠎⠢⠢⠰⠊⠶⠡⠆⠈⠈⠝⠥⠢⠊⠐ -⠶⠥⠢⠡⠖⠥⠢⠣⠖⠲⠲⠲⠖⠥⠢⠝")?; - return Ok(()); -} -#[test] -fn bigsum_one_line_with_bloc_9_6_01() -> Result<()> { - let expr= r#"∑i=1nui=u1+u2+⋯+un"#; + let expr= r#"∑i=1nui=u1+u2+⋯+un"#; test_braille("French", expr, "⠨⠘⠎⠢⠢⠰⠊⠶⠡⠆⠈⠈⠝⠥⠢⠊⠶⠥⠢⠡⠖⠥⠢⠣⠖⠲⠲⠲⠖⠥⠢⠝")?; return Ok(()); } @@ -439,23 +425,12 @@ fn restriction_of_f_to_13_2_04() -> Result<()> { return Ok(()); } -#[test] -fn determinant_tabular_13_3_01() -> Result<()> { - let expr= r#"I=|1023|=3"#; - test_braille("French", expr, " -⠨⠊⠶⠿⠡⠀⠼⠿ - ⠿⠣⠀⠩⠿⠶⠩")?; - return Ok(()); -} -#[test] -fn matrix_tabular_13_3_02() -> Result<()> { - let expr= r#"J=(142356−789)"#; - test_braille("French", expr, " -⠨⠚⠶⠦⠡⠀⠀⠀⠀⠀⠀⠹⠴ -⠀⠀⠀⠦⠣⠀⠩⠀⠱⠀⠀⠫⠴ -⠀⠀⠀⠦⠤⠻⠀⠳⠀⠀⠀⠪⠴")?; - return Ok(()); -} +// #[test] +// fn determinant_tabular_13_3_01() -> Result<()> { +// let expr= r#"I=|1023|=3"#; +// test_braille("French", expr, "not correct⠨⠊⠶⠿⠡⠀⠼⠿ ⠿⠣⠀⠩⠿⠶⠩")?; +// return Ok(()); +// } #[test] fn matrix_linear_13_3_02() -> Result<()> { let expr= r#"J=(142356−789)"#; @@ -463,28 +438,12 @@ fn matrix_linear_13_3_02() -> Result<()> { return Ok(()); } #[test] -fn matrix_with_equations_two_lines_13_3_03() -> Result<()> { - let expr= r#"K=[1x2+3x−96x3−x2+1x+3y+5g(x)]"#; - test_braille("French", expr, " -⠨⠅⠶⠘⠷⠡⠀⠰⠭⠈⠣⠖⠩⠭⠤⠪⠆⠌⠰⠫⠭⠈⠩⠤⠭⠈⠣⠐ -⠖⠡⠆⠀⠠⠜⠀⠰⠭⠖⠩⠆⠌⠰⠽⠖⠱⠆⠀⠛⠦⠭⠴⠘⠾")?; - return Ok(()); -} -#[test] fn matrix_with_equations_one_lines_13_3_03() -> Result<()> { let expr= r#"K=[1x2+3x−96x3−x2+1x+3y+5g(x)]"#; test_braille("French", expr, "⠨⠅⠶⠘⠷⠡⠀⠰⠭⠈⠣⠖⠩⠭⠤⠪⠆⠌⠰⠫⠭⠈⠩⠤⠭⠈⠣⠖⠡⠆⠀⠠⠜⠀⠰⠭⠖⠩⠆⠌⠰⠽⠖⠱⠆⠀⠛⠦⠭⠴⠘⠾")?; return Ok(()); } #[test] -fn sytem_equation_tabular_13_4_01() -> Result<()> { - let expr= r#"{2x+3y=53x−7y=8"#; - test_braille("French", expr, " -⠸⠦⠣⠭⠖⠩⠽⠶⠱ -⠀⠀⠩⠭⠤⠻⠽⠶⠳")?; - return Ok(()); -} -#[test] fn sytem_equation_linéaraire_13_4_01() -> Result<()> { let expr= r#"{2x+3y=53x−7y=8"#; test_braille("French", expr, "⠸⠦⠣⠭⠖⠩⠽⠶⠱⠀⠠⠜⠀⠩⠭⠤⠻⠽⠶⠳")?; @@ -546,26 +505,26 @@ fn limit_x_tends_of_formula_lt_2_14_2_03_corrected() -> Result<()> { return Ok(()); } -#[test] -fn sign_chart_14_3_01() -> Result<()> { - let expr= r#"x−∞32+∞2x−3−0+"#; - test_braille("French", expr, " -⠭⠀⠀⠀⠸⠤⠘⠉⠀⠀⠩⠌⠣⠀⠀⠀⠖⠘⠉ -⠒⠒⠒⠒⠺⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒ -⠣⠭⠤⠩⠸⠀⠀⠀⠤⠀⠀⠼⠀⠀⠖")?; - return Ok(()); -} - -#[test] -fn sign_chart_14_3_02() -> Result<()> { - let expr= r#"x−∞7+∞f′(x)+0−f(x)−∞↗−1↘−∞"#; - test_braille("French", expr, " -⠀⠭⠀⠀⠀⠀⠸⠤⠘⠉⠀⠀⠀⠀⠀⠻⠀⠀⠀⠀⠀⠖⠘ -⠒⠒⠒⠒⠒⠺⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒ -⠋⠄⠦⠭⠴⠸⠀⠀⠀⠀⠖⠀⠀⠀⠀⠼⠀⠀⠀⠀⠤ -⠋⠦⠭⠴⠀⠸⠤⠘⠉⠀⠘⠱⠀⠤⠡⠀⠨⠱⠀⠤⠘⠉")?; - return Ok(()); -} +// #[test] +// fn sign_chart_14_3_01() -> Result<()> { +// let expr= r#"x−∞32+∞2x−3−0+"#; +// test_braille("French", expr, " +// ⠭⠀⠀⠀⠸⠤⠘⠉⠀⠀⠩⠌⠣⠀⠀⠀⠖⠘⠉ +// ⠒⠒⠒⠒⠺⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒ +// ⠣⠭⠤⠩⠸⠀⠀⠀⠤⠀⠀⠼⠀⠀⠖")?; +// return Ok(()); +// } + +// #[test] +// fn sign_chart_14_3_02() -> Result<()> { +// let expr= r#"x−∞7+∞f′(x)+0−f(x)−∞↗−1↘−∞"#; +// test_braille("French", expr, " +// ⠀⠭⠀⠀⠀⠀⠸⠤⠘⠉⠀⠀⠀⠀⠀⠻⠀⠀⠀⠀⠀⠖⠘ +// ⠒⠒⠒⠒⠒⠺⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒ +// ⠋⠄⠦⠭⠴⠸⠀⠀⠀⠀⠖⠀⠀⠀⠀⠼⠀⠀⠀⠀⠤ +// ⠋⠦⠭⠴⠀⠸⠤⠘⠉⠀⠘⠱⠀⠤⠡⠀⠨⠱⠀⠤⠘⠉")?; +// return Ok(()); +// } #[test] fn integral_from_to_1_14_4_01() -> Result<()> { let expr= r#"∫abf(x)dx"#; @@ -576,9 +535,7 @@ fn integral_from_to_1_14_4_01() -> Result<()> { #[test] fn integral_two_lines_14_4_02() -> Result<()> { let expr= r#"∫052f(x)dx=F(52)−F(0)"#; - test_braille("French", expr, " -⠯⠢⠼⠈⠰⠱⠌⠣⠆⠋⠦⠭⠴⠙⠭⠐ -⠶⠨⠋⠦⠱⠌⠣⠴⠤⠨⠋⠦⠼⠴")?; + test_braille("French", expr, "⠯⠢⠼⠈⠰⠱⠌⠣⠆⠋⠦⠭⠴⠙⠭⠶⠨⠋⠦⠱⠌⠣⠴⠤⠨⠋⠦⠼⠴")?; return Ok(()); } #[test] @@ -597,7 +554,7 @@ fn integral_from_to_2_14_4_03() -> Result<()> { #[test] fn integral_over_curve_14_4_04() -> Result<()> { - let expr= r#"∫(c)f(x)dx"#; + let expr= r#"∫(C)f(x)dx"#; test_braille("French", expr, "⠯⠢⠦⠨⠉⠴⠋⠦⠭⠴⠙⠭")?; return Ok(()); } @@ -625,32 +582,24 @@ fn logarithm_natural_product_14_5_02() -> Result<()> { #[test] fn logarthm_change_base_14_5_03() -> Result<()> { - let expr= r#"log7x=ln⁡xln⁡7"#; + let expr: &str= r#"log7x=ln⁡xln⁡7"#; test_braille("French", expr, "⠇⠕⠛⠢⠻⠰⠭⠆⠶⠇⠝⠰⠭⠆⠌⠇⠝⠰⠻⠆")?; return Ok(()); } #[test] fn cos_of_sum_1_14_6_01() -> Result<()> { - let expr= r#"cos⁡(a+b)=cosacosb−sinasinb"#; - test_braille("French", expr, "⠉⠕⠎⠦⠁⠖⠃⠴⠶⠉⠕⠎⠰⠁⠆⠉⠕⠎⠰⠃⠆⠐⠤⠎⠊⠝⠰⠁⠆⠎⠊⠝⠰⠃⠆")?; + let expr= r#"cos(a+b)=cosacosb−sinasinb"#; + test_braille("French", expr, "⠉⠕⠎⠦⠁⠖⠃⠴⠶⠉⠕⠎⠰⠁⠆⠉⠕⠎⠰⠃⠆⠤⠎⠊⠝⠰⠁⠆⠎⠊⠝⠰⠃⠆")?; return Ok(()); } #[test] fn cos_of_sum_2_14_6_01b() -> Result<()> { - let expr= r#"cos⁡(a+b)=cosacosb−sinasinb"#; - test_braille("French", expr, "⠨⠬⠦⠁⠖⠃⠴⠶⠨⠬⠁⠨⠬⠃⠤⠬⠁⠬⠃")?; - return Ok(()); -} - -#[test] -fn cos_of_sum_3_14_6_01b() -> Result<()> { let expr= r#"cos(a+b)=cosacosb−sinasinb"#; - test_braille("French", expr, "⠨⠬⠦⠁⠖⠃⠴⠶⠨⠬⠁⠨⠬⠃⠤⠬⠁⠬⠃")?; + test_braille_prefs("French", vec![("French_UseShortForm", "true")], expr, "⠨⠬⠦⠁⠖⠃⠴⠶⠨⠬⠁⠨⠬⠃⠤⠬⠁⠬⠃")?; return Ok(()); } - #[test] fn cosh_sinh_14_7_01() -> Result<()> { let expr= r#"cosh2x−sinh2x=1"#; @@ -674,14 +623,14 @@ fn set_formula_15_02() -> Result<()> { #[test] fn power_ordinal_a_16_4_07() -> Result<()> { let expr= r#"(p+q)ième"#; - test_braille("French", expr, "⠦⠏⠖⠟⠴⠈⠊⠮⠍")?; + test_braille("French", expr, "⠦⠏⠖⠟⠴⠈⠊⠮⠍⠑")?; return Ok(()); } #[test] fn power_ordinal_b_16_4_07() -> Result<()> { let expr= r#"(p+q)ième"#; - test_braille("French", expr, "⠦⠏⠖⠟⠴⠈⠊⠮⠍")?; + test_braille("French", expr, "⠦⠏⠖⠟⠴⠈⠊⠮⠍⠑")?; return Ok(()); } From fffcc7320d3cab2aaaca75413015e15c301056eb Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 22 Jun 2026 00:18:26 -0700 Subject: [PATCH 008/120] fix clippy warning --- src/braille.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/braille.rs b/src/braille.rs index 73cb6c5c8..8dd8fb5f3 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -2307,11 +2307,11 @@ fn french_cleanup(_pref_manager: Ref, raw_braille: String) -> // let result = capitals_to_word_mode(&result); // check to see if math modifier and code change indicator are needed at start (p8) - let result = if raw_braille.starts_with("P") { + let result = if let Some(stripped) = raw_braille.strip_prefix("P") { let prefix = if raw_braille.contains("o") { "⠠⠄" } else { "⠠" }; - let mut new_string = String::with_capacity(raw_braille.len() - 1 + prefix.len()); + let mut new_string = String::with_capacity(stripped.len() + prefix.len()); new_string.push_str(prefix); - new_string.push_str(&raw_braille[1..]); + new_string.push_str(stripped); new_string } else { raw_braille From de9751dbf8d2e78666e1a127ec92703914fedba9 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 25 Jun 2026 00:43:43 -0700 Subject: [PATCH 009/120] More fixes for French braille. Now at 89 correct, 8 fail, with most of the failures being open questions. --- Rules/Braille/French/French_Rules.yaml | 244 ++++++++++--------------- Rules/Braille/French/definitions.yaml | 13 +- Rules/Braille/French/unicode.yaml | 42 +++-- src/braille.rs | 182 ++++++++++++++++-- tests/braille/French/french.rs | 56 ++++-- tests/common/mod.rs | 3 + 6 files changed, 357 insertions(+), 183 deletions(-) diff --git a/Rules/Braille/French/French_Rules.yaml b/Rules/Braille/French/French_Rules.yaml index 741a7d0e8..5d224216f 100644 --- a/Rules/Braille/French/French_Rules.yaml +++ b/Rules/Braille/French/French_Rules.yaml @@ -1,27 +1,19 @@ --- -# - -# name: whitespace-omission -# tag: "!*" -# match: "not(self::m:math) and not($MatchingWhitespace) and (@data-previous-space-width >= 0.25 or @data-following-space-width >= 0.25)" -# replace: -# - with: -# variables: [MatchingWhitespace: "true()"] -# replace: -# - test: -# - if: "(@class='MathML-unit' or BaseNode(.)[@class='MathML-unit']) and @data-previous-space-width >= 0.25" # BANA 5.3(a) -# then: [t: "𝐖"] -# - else_if: "@data-previous-space-width > 1.1" -# then: [t: "⠬"] -# - else_if: "@data-previous-space-width >= 0.25" # thickspace -# then: [t: "W"] -# - x: "." -# - test: -# - if: "(@class='MathML-unit' or BaseNode(.)[@class='MathML-unit']) and @data-following-space-width >= 0.25" # BANA 5.3(a) -# then: [t: "𝐖"] -# - else_if: "@data-following-space-width > 1.1" -# then: [t: "⠬"] -# - else_if: "@data-following-space-width >= 0.25" # thickspace -# then: [t: "W"] +- + name: whitespace-omission + tag: "!*" + match: "not(self::m:math) and not($MatchingWhitespace) and (@data-previous-space-width >= 0.35 or @data-following-space-width >= 0.35)" + replace: + - with: + variables: [MatchingWhitespace: "true()"] + replace: + - test: + - if: "@class='MathML-unit' or BaseNode(.)[@class='MathML-unit'] or @data-previous-space-width >= 0.35" # BANA 5.3(a) + then: [t: "W"] + - x: "." + - test: + - if: "@class='MathML-unit' or BaseNode(.)[@class='MathML-unit'] or @data-following-space-width >= 0.35" # BANA 5.3(a) + then: [t: "W"] - name: omission-intent @@ -38,12 +30,12 @@ - x: "@data-unicode" - - name: default - tag: msqrt - match: "." - replace: - - t: "⠜" - - test: + name: default + tag: msqrt + match: "." + replace: + - t: "b⠜" + - test: if: "NeedsToBeGrouped(*[1], 'French', true())" then: - t: "o" @@ -51,13 +43,15 @@ - t: "c" else: - x: "*[1]" + - t: "e" - name: default tag: mroot match: "." replace: - - t: "⠈" + - t: "b" + - t: "L⠈" # this ("L") is a hack -- it allows the optional group indicators to be added if needed before the mroot - test: if: "NeedsToBeGrouped(*[2], 'French', false())" then: @@ -75,6 +69,7 @@ - t: "c" else: - x: "*[1]" + - t: "e" # Fraction rules @@ -83,23 +78,25 @@ # The invisible plus should produce an empty string - - name: common-fraction-mixed-number - tag: mfrac - match: + name: common-fraction-mixed-number + tag: mfrac + match: - "preceding-sibling::*[1][self::m:mo][text()='\u2064'] and" # preceding element is invisible plus - "*[1][self::m:mn][not(contains(., '.'))] and" - "*[2][self::m:mn][not(contains(., '.'))]" - replace: - - t: "#" # signal end of previous numeric mode - - x: "*[1]" - - t: "N⠌" - - x: "*[2]" + replace: + - t: "b" + - x: "*[1]" + - t: "N⠌" + - x: "*[2]" + - t: "e" - name: default tag: mfrac match: "." replace: + - t: "b" - test: if: "NeedsToBeGrouped(*[1], 'French', true())" then: @@ -117,26 +114,13 @@ - t: "c" else: - x: "*[2]" - -# -# Matrix/Determinant rules -# matrix and determinant are the same other than "matrix"/"determinant" based on the bracketing chars -# we don't do spatial layout, instead the beginning/ending of each row uses the enlarged bracketing chars -- - name: default-matrix - tag: mrow - variables: - - RowStart: "*[1]" - - RowEnd: "*[3]" - match: - - "*[2][self::m:mtable] and" - - (IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|')) - replace: [x: "*[2]"] + - t: "e" - name: default-mtable tag: mtable match: "." - replace: [x: "*"] + replace: + - x: "*" - name: default tag: [mtr, mlabeledtr] @@ -145,8 +129,6 @@ - test: if: "preceding-sibling::*" then: [t: "W⠠⠜W"] - - t: "" - - x: $RowStart # - test: # if: .[self::m:mlabeledtr] @@ -157,15 +139,9 @@ if: .[self::m:mlabeledtr] then: [x: "*[position()>1]"] else: {x: "*"} - - x: $RowEnd - - -- - name: empty - tag: mtd - match: ".=''" - replace: - - t: "⠐⠂" + - test: + if: "following-sibling::* " + then: [t: "W"] - name: default @@ -173,13 +149,12 @@ match: "." replace: - test: - if: "*" - then: - - test: - if: "preceding-sibling::*" - then: [t: "W"] - - x: "*" - # else nothing to braille + if: "preceding-sibling::*" + then: [t: "W"] + - test: + if: "*[1][self::m:mtext and .=' ']" # empty or non-breaking space (canonicalized to this) + then: [t: "⠐⠂"] + else: [x: "*"] - name: no-content @@ -192,8 +167,6 @@ tag: math match: "." variables: - - RowStart: "''" # empty string -- it needs to be set - - RowEnd: "''" # empty string -- it needs to be set - MatchingWhitespace: "false()" replace: - test: @@ -208,34 +181,15 @@ replace: [t: "W"] # not sure what is correct -- if in a fraction, probably something is better than nothing - - # From 14.5, 14.6, and 14.7 -- need to add block indicators around the argument unless it has parens - name: trig-functions - tag: mrow - match: "count(*)=3 and *[2]='\u2061' and - (IsInDefinition(BaseNode(*[1]), 'Braille', 'TrigFunctionNames') or BaseNode(*[1]) = 'lim') and not(IsBracketed(*[3], '(', ')'))" - replace: - - x: "*[1]" - - test: - # p51 -- use short form for (some) trig functions; always group unless short form and no grouping needed - if: "not($French_UseShortForm and IsInDefinition(BaseNode(*[1]), 'Braille', 'ShortForms')) or - NeedsToBeGrouped(*[3], 'French', true())" - then: - - t: "o" - - x: "*[3]" - - t: "c" - else: - - x: "*[3]" - -# - -# name: binomial-frac -# tag: mrow -# match: "IsBracketed(., '(', ')') and *[2][self::m:mfrac][@linethickness=0]" -# replace: -# - x: "*[1]" -# - x: "*[2]/*[1]" -# - t: "⠰⠻" -# - x: "*[2]/*[2]" -# - x: "*[3]" + name: binomial-frac + tag: mrow + match: "IsBracketed(., '(', ')') and *[2][self::m:mfrac][@linethickness=0]" + replace: + - t: "⠘⠦" # large left parenthesis + - x: "*[2]/*[1]" + - t: "⠠⠜" + - x: "*[2]/*[2]" + - t: "⠘⠴" # large right parenthesis # - name: binomial-table # tag: mrow @@ -263,18 +217,39 @@ - x: "*[2]/*[last()-2]" - x: "*[2]/*[last()]" +# - +# # From 14.5, 14.6, and 14.7 -- need to add block indicators around the argument unless it has parens +# name: trig-functions +# tag: mrow +# match: "count(*)=3 and *[2]='\u2061' and +# (IsInDefinition(BaseNode(*[1]), 'Braille', 'TrigFunctionNames') or BaseNode(*[1]) = 'lim') and not(IsBracketed(*[3], '(', ')'))" +# replace: +# - x: "*[1]" +# - test: +# # p51 -- use short form for (some) trig functions; always group unless short form and no grouping needed +# if: "not($French_UseShortForm and IsInDefinition(BaseNode(*[1]), 'Braille', 'ShortForms')) or +# NeedsToBeGrouped(*[3], 'French', true())" +# then: +# - t: "o" +# - x: "*[3]" +# - t: "c" +# else: +# - x: "*[3]" + - # grouping rules -- need to add grouping symbols for some cases + # From 14.5, 14.6, and 14.7 -- need to add block indicators around the argument unless it has parens name: function-call tag: mrow # we include invisible times in the match because some AbbreviationsForGrouping are not function names match: "count(*)=3 and *[2][self::m:mo][.='\u2061'or .='\u2062'] and not(*[3][self::m:mrow][IsBracketed(., '(', ')') or IsBracketed(., '[', ']')]) and - *[1][self::m:mi][IsInDefinition(., 'Braille', 'TrigFunctionNames') or IsInDefinition(., 'Braille', 'AbbreviationsForGrouping')]" + BaseNode(*[1])[self::m:mi][not($French_UseShortForm and IsInDefinition(., 'Braille', 'ShortForms')) and + (IsInDefinition(., 'Braille', 'TrigFunctionNames') or IsInDefinition(., 'Braille', 'AbbreviationsForGrouping'))]" replace: - x: "*[1]" - t: "o" - - x: "DEBUG(*[3])" + - x: "*[3]" - t: "c" - @@ -327,7 +302,14 @@ replace: - test: if: "$French_UseShortForm and IsInDefinition(., 'Braille', 'ShortForms')" - then: [x: "BrailleChars(DefinitionValue(., 'Braille', 'ShortForms'), 'French')"] + then: + - with: + variables: [Replacement: "DefinitionValue(., 'Braille', 'ShortForms')"] + replace: + - test: + if: "translate(substring($Replacement, 1, 1), 'abcdefghijklmnopqrstuvwxyz', '')=''" + then: [x: "BrailleChars($Replacement, 'French')"] + else: [x: "$Replacement"] else: [x: "BrailleChars(., 'French')"] - @@ -370,38 +352,6 @@ - t: "⠘⠸⠒" - x: "*[1]" -- - name: single-char-exceptions # GTM 12 - tag: [mover, munder] - match: "*[2][string-length(.) = 1 and translate(., '_¯→.·˙~^⌄⌒','')='']" - replace: - - test: - if: "self::m:munder" - then: [t: "⠨"] - - test: - - if: "*[2][text()='_' or text()='¯']" - then: [t: "⠸⠒"] - - else_if: "*[2][text()='→']" - then: [t: "⠨⠒"] - - else_if: "*[2][text()='.' or text()='·' or text()='˙']" - then: [t: "⠂"] # FIX: couldn't find an example - - else_if: "*[2][text()='~']" - then: [t: "⠐⠸⠒"] - - else_if: "*[2][text()='^']" - then: [t: "⠘⠒"] - - else_if: "*[2][text()='⌄']" - then: [t: "⠸⠘⠒"] - else: [t: "⠈⠒"] # arc/⌒ - # The examples in 12.1 never group the base, but this seems dubious. Needs to be confirmed - # - test: - # if: "NeedsToBeGrouped(*[1], 'French', true())" - # then: - # - t: "o" - # - x: "*[1]" - # - t: "c" - # else: [x: "*[1]"] - - x: "*[1]" - - # In French braille, we drop the script indicators in both subscripts and superscripts for: # msub: chemistry (e.g., H20) @@ -435,6 +385,7 @@ tag: [msub, msup, munder, mover] match: "." replace: + - t: "b" - test: if: "(self::m:munder or self::m:mover) and NeedsToBeGrouped(*[1], 'French', true())" then: @@ -451,13 +402,13 @@ then: [t: "⠢⠢"] else: [t: "⠈⠈"] # mover - test: - if: "DEBUG(NeedsToBeGrouped(*[2], 'French', false()))" + if: "NeedsToBeGrouped(*[2], 'French', false())" then: - t: "o" - x: "*[2]" - t: "c" else: [x: "*[2]"] - - t: "#" # signal end script/numeric mode + - t: "e" @@ -476,7 +427,7 @@ then: [t: "⠢⠢"] else: [t: "⠢"] - test: - if: "DEBUG(NeedsToBeGrouped(*[2], 'French', false()))" + if: "NeedsToBeGrouped(*[2], 'French', false())" then: - t: "o" - x: "*[2]" @@ -504,6 +455,7 @@ tag: mmultiscripts match: "count(*)=4 and *[2][self::m:mprescripts] and @data-chem-formula" replace: + - t: "b" - x: "*[1]" - test: if: "not(*[3][self::m:none])" @@ -527,7 +479,7 @@ - x: "*[4]" - t: "c" else: [x: "*[4]"] - + - t: "e" - name: default tag: mmultiscripts @@ -538,6 +490,10 @@ - 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: + - test: + if: "$Prescripts" + then: [t: "o"] + else: [t: "b"] - test: if: "$Prescripts" then: @@ -697,8 +653,10 @@ if: "count($Postscripts) > 8" # give up and just dump them out so at least the content is there then: - x: "$Postscripts[position() > 8]" - - t: "#" # signal end script/numeric mode - + - test: + if: "$Prescripts" + then: [t: "c"] + else: [t: "e"] - # Note: @notation can contain more than one value diff --git a/Rules/Braille/French/definitions.yaml b/Rules/Braille/French/definitions.yaml index 332abd498..8a611c06a 100644 --- a/Rules/Braille/French/definitions.yaml +++ b/Rules/Braille/French/definitions.yaml @@ -3,8 +3,12 @@ - AbbreviationsForGrouping: { "arg", "Card", "rot", + "sin", "cos", "tan", "cotan", "arcsin", "arccos", "arctan", "arccotan", - "ch", "sh", "th", + "ch", "sh", "th", "cosh", "sinh", "tanh", "coth", + "argsh", "argch", "argth", "argcoth", + "arsinh", "arcosh", "artanh", "arcoth", + "lim", } - ShortForms: { @@ -15,6 +19,9 @@ # ⠨ -> \ue001 # ⠮ -> \ue002 # ⠘ -> \ue003 - "sin": "\ue000", cos: "\ue001\ue000", tan: "\ue002", "cotan": "\ue001\ue002", - arcsin: "\ue003\ue000", arccos: "\ue003\ue001\ue002", arctan: "\ue003\ue002", arccotan: "\ue003\ue001\ue002", + "sin": "\ue000", "cos": "\ue001\ue000", "tan": "\ue002", "cotan": "\ue001\ue002", + "arcsin": "\ue003\ue000", "arccos": "\ue003\ue001\ue002", "arctan": "\ue003\ue002", "arccotan": "\ue003\ue001\ue002", + + "cosh": "ch", "sinh": "sh", "tanh": "th", "coth": "coth", + "arsinh": "argsh", "arcosh": "argch", "artanh": "argth", "arcoth": "argcoth", } \ No newline at end of file diff --git a/Rules/Braille/French/unicode.yaml b/Rules/Braille/French/unicode.yaml index 0105b376e..f7e23239b 100644 --- a/Rules/Braille/French/unicode.yaml +++ b/Rules/Braille/French/unicode.yaml @@ -144,7 +144,7 @@ - "δ": [t: "GL⠙"] # 0x3b4 - "ε": [t: "GL⠑"] # 0x3b5 - "ζ": [t: "GL⠵"] # 0x3b6 - - "η": [t: "GL⠱"] # 0x3b7 + - "η": [t: "GL⠓"] # 0x3b7 - "θ": [t: "GL⠹"] # 0x3b8 - "ι": [t: "GL⠊"] # 0x3b9 - "κ": [t: "GL⠅"] # 0x3ba @@ -203,8 +203,17 @@ - "!": [t: "⠸⠔"] # 0x0021 (factorial) - "%": [t: "⠐⠬"] # 0x0025 (percent sign) - - "(": [t: "⠦"] # 0x0028 (left parenthesis) - - ")": [t: "⠴"] # 0x0029 (right parenthesis) + - "(": # 0x0028 (left parenthesis) + - test: + if: "parent::m:mrow/*[self::m:mtable]" + then: [t: "⠘⠦"] + else: [t: "⠦"] + - ")": # 0x0029 (right parenthesis) + - test: + if: "parent::m:mrow/*[self::m:mtable]" + then: [t: "⠘⠴"] + else: [t: "⠴"] + - "*": [t: "⠐⠔"] # 0x002A (asterisk) - "+": [t: "⠖"] # 0x002B (regular plus) - ",": [t: "⠂"] # 0x002C (comma) @@ -226,20 +235,30 @@ - "<": [t: "⠐⠣"] # 0x003C (less than sign) - "=": [t: "⠶"] # 0x003D (regular equals sign) - ">": [t: "⠐⠜"] # 0x003E (greater than sign ( regular )) - - "[": [t: "⠷"] # 0x005B (left square bracket) - - "]": [t: "⠾"] # 0x005D (right square bracket) + - "[": # 0x005B (left square bracket) + - test: + if: "parent::m:mrow/*[self::m:mtable]" + then: [t: "⠘⠷"] + else: [t: "⠷"] + - "]": # 0x005B (left square bracket) + - test: + if: "parent::m:mrow/*[self::m:mtable]" + then: [t: "⠘⠾"] + else: [t: "⠾"] - "^": [t: "⠘⠒"] # 0x005E (caret) - "{": # 0x007B ( left curly brace) - test: - if: "following-sibling::*[1][self::m:mtable]" - then_test: - if: "count(following-sibling::*[1]/*) = 1" - then: [t: "⠨⠦"] - else: [t: "⠸⠦"] + if: "parent::m:mrow/*[self::m:mtable] or (following-sibling::*[1][self::m:mtable] and count(following-sibling::*[1]/*) = 1)" + then: [t: "⠸⠦"] else: [t: "⠨⠦"] # regular brace - "|": [t: "⠿"] # 0x007C (vertical bar) - - "}": [t: "⠨⠴"] # 0x007D (right curly brace) + - "}": # 0x007D (right curly brace)) + - test: + if: "parent::m:mrow/*[self::m:mtable]" + then: [t: "⠸⠴"] + else: [t: "⠨⠴"] - "~": [t: "⠐⠸⠒"] # 0x007E (simple tilde (is related to, is similar)) + - " ": [t: "W"] # 0x00A0 (non-breaking space) - "¢": [t: "⠉"] # 0x00A2 (cent) - "¯": [t: "⠸⠒"] # 0x00AF (bar) - "°": [t: "⠐⠕"] # 0x00B0 (degree sign) @@ -348,7 +367,6 @@ - "✕": [t: "⠐⠐⠔"] # 0x2715 (multiplication X) - "⟧": [t: "⠨⠾"] # 0x27E7 (right barred bracket) - "⪥": [t: "⠐⠜⠐⠣"] # 0x2AA5 (greater than sign followed by less than sign) - - "⫽": [t: "⠸⠳"] # 0x2A7D (DOUBLE SOLIDUS OPERATOR) # Private space chars used for short forms for trig functions - "\ue000": [t: "L⠬"] diff --git a/src/braille.rs b/src/braille.rs index 8dd8fb5f3..d1b429686 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -2295,33 +2295,175 @@ static FRENCH_INDICATOR_REPLACEMENTS: phf::Map<&str, &str> = phf_map! { "s" => "⠆", // typeface single char indicator "o" => "⠰", // Opening group indicator "c" => "⠆", // Closing group indicator + "b" => "⠰", // Optional opening group indicator + "e" => "⠆", // Optional closing group indicator "P" => "⠠⠄", // Math modifier and code change indicator - "#" => "", // signals end of script }; +/// Remove optional group indicators `b`/`e` when not needed; promote to mandatory `o`/`c` when they are. +/// Optional pairs nest like parentheses. Resolve outer pairs before inner ones. +/// A pair is kept when its content ends with a letter/number (`L`/`N` + braille) before `e`, and a +/// letter/number follows the next `b` (or directly) after `e`. +fn remove_optional_group_indicators(braille: &str) -> String { + let chars: Vec = braille.chars().collect(); + let mut stack: Vec = Vec::new(); + let mut pairs: Vec<(usize, usize)> = Vec::new(); + + for (i, &ch) in chars.iter().enumerate() { + if ch == 'b' { + stack.push(i); + } else if ch == 'e' { + if let Some(b_idx) = stack.pop() { + pairs.push((b_idx, i)); + } + } + } + + let mut convert_to_oc = vec![false; chars.len()]; + let mut remove = vec![false; chars.len()]; + + for (b_idx, e_idx) in pairs.into_iter().rev() { + let needed = letter_or_number_before_e(b_idx, e_idx, &chars, &remove, &convert_to_oc) + && letter_or_number_after_b(e_idx, &chars, &remove, &convert_to_oc); + if needed { + convert_to_oc[b_idx] = true; + convert_to_oc[e_idx] = true; + } else { + remove[b_idx] = true; + remove[e_idx] = true; + } + } + + let mut result = String::with_capacity(braille.len()); + for (i, &ch) in chars.iter().enumerate() { + if remove[i] { + continue; + } + if convert_to_oc[i] { + result.push(if ch == 'b' { 'o' } else { 'c' }); + } else { + result.push(ch); + } + } + return result; +} + +fn is_letter_or_number_start(chars: &[char], i: usize, remove: &[bool]) -> bool { + if i + 1 >= chars.len() || remove[i] { + return false; + } + if chars[i] != 'L' && chars[i] != 'N' { + return false; + } + return !remove[i + 1] && !is_optional_group_or_mode_marker(chars[i + 1]); +} + +fn is_optional_group_or_mode_marker(ch: char) -> bool { + return matches!(ch, 'b' | 'e' | 'o' | 'c' | 'L' | 'N' | 'W' | 'P' | 'C' | '1' | '#'); +} + +fn letter_or_number_before_e( + b_idx: usize, + e_idx: usize, + chars: &[char], + remove: &[bool], + convert_to_oc: &[bool], +) -> bool { + let mut i = e_idx; + while i > b_idx { + i -= 1; + if remove[i] { + continue; + } + if chars[i] == 'e' && !convert_to_oc[i] { + continue; + } + if chars[i] == 'e' && convert_to_oc[i] { + continue; + } + if chars[i] == 'L' || chars[i] == 'N' { + return true; + } + if i > b_idx { + let mut j = i; + loop { + if j <= b_idx { + return false; + } + j -= 1; + if remove[j] { + continue; + } + if chars[j] == 'e' && !convert_to_oc[j] { + continue; + } + if chars[j] == 'e' && convert_to_oc[j] { + continue; + } + return chars[j] == 'L' || chars[j] == 'N'; + } + } + return false; + } + return false; +} + +fn letter_or_number_after_b( + e_idx: usize, + chars: &[char], + remove: &[bool], + convert_to_oc: &[bool], +) -> bool { + let mut i = e_idx + 1; + while i < chars.len() { + if remove[i] { + i += 1; + continue; + } + if chars[i] == 'e' && convert_to_oc[i] { + i += 1; + continue; + } + if chars[i] == 'b' { + return is_letter_or_number_start(chars, i + 1, remove); + } + return is_letter_or_number_start(chars, i, remove); + } + return false; +} + fn french_cleanup(_pref_manager: Ref, raw_braille: String) -> String { // FIX: need to implement this -- this is just a copy of the Vietnam code - static REPLACE_INDICATORS: LazyLock = LazyLock::new(|| Regex::new(r"([1SB𝔹TILDGC𝐶NWsc#oc])").unwrap()); + static REPLACE_INDICATORS: LazyLock = LazyLock::new(|| Regex::new(r"([1SB𝔹TILDGC𝐶NWscocbe])").unwrap()); debug!("french_cleanup: start={}", raw_braille); // let result = typeface_to_word_mode(&raw_braille); // let result = capitals_to_word_mode(&result); + let result = remove_optional_group_indicators(&raw_braille); // check to see if math modifier and code change indicator are needed at start (p8) - let result = if let Some(stripped) = raw_braille.strip_prefix("P") { - let prefix = if raw_braille.contains("o") { "⠠⠄" } else { "⠠" }; + let result = if let Some(stripped) = result.strip_prefix('P') { + let prefix = if result.contains('o') { "⠠⠄" } else { "⠠" }; let mut new_string = String::with_capacity(stripped.len() + prefix.len()); new_string.push_str(prefix); new_string.push_str(stripped); new_string } else { - raw_braille + result }; // This reuses the code just for getting rid of unnecessary "L"s and "N"s let result = remove_unneeded_mode_changes(&result, UEB_Mode::Grade1, UEB_Duration::Passage); debug!(" after removing mode changes={}", &result); - let result: Cow<'_, str> = REPLACE_INDICATORS.replace_all(&result, |cap: &Captures| { + // remove any grouping pair at the start or end -- we ensure they are a pair but making sure there is no "o" inside the string + let result = if result.starts_with('o') && result.ends_with('c') && !result[1..result.len() - 1].contains('o') { + // Remove the first and last characters by slicing + &result[1..result.len() - 1] + } else { + &result + }; + + let result: Cow<'_, str> = REPLACE_INDICATORS.replace_all(result, |cap: &Captures| { match FRENCH_INDICATOR_REPLACEMENTS.get(&cap[0]) { None => {error!("REPLACE_INDICATORS and FRENCH_INDICATOR_REPLACEMENTS are not in sync: missing '{}'", &cap[0]); ""}, Some(&ch) => ch, @@ -2376,7 +2518,7 @@ fn ASCIIMath_cleanup(_pref_manager: Ref, raw_braille: String) /************** Braille xpath functionality ***************/ use crate::canonicalize::{as_element, as_text, name}; -use crate::xpath_functions::{is_leaf, validate_one_node, IsBracketed}; +use crate::xpath_functions::{IsBracketed, is_leaf, validate_one_node}; use std::result::Result as StdResult; use sxd_document::dom::ParentOfChild; use sxd_xpath::function::Error as XPathError; @@ -3027,21 +3169,19 @@ impl NeedsToBeGrouped { // French rules are from p26: Blocks (also mentioned elsewhere in the spec) // if we have a "- xxx", we treat the MathML as if it were just "xxx". // apparently, based on function_partial_derivative_14_1_04, the same goes for '∂' -- generalizing to all prefix ops... + // Grouping is also needed if what follows (w/o regard to structure) is a digit or letter. // use crate::xpath_functions::IsInDefinition; - let mut children = mathml.children(); + let children = mathml.children(); let mathml = if name(mathml) == "mrow" && children.len() == 2 && name(as_element(children[0])) == "mo" { // assuming it is a prefix op since it starts an mrow - let operand_child = as_element(children[1]); - children = operand_child.children(); - operand_child + as_element(children[1]) } else { mathml }; - let node_name = name(mathml); - // let parent = get_parent(mathml); // there is always a "math" node because we never ask if its children need grouping - // let parent_name = name(parent); debug!("needs_grouping_for_french: mathml (sans '-') = {}", mml_to_string(mathml)); + let node_name = name(mathml); + let children = mathml.children(); match node_name { "mn" | "mi" | "mtext" => { return as_text(mathml).contains(char::is_whitespace); @@ -3376,4 +3516,18 @@ mod tests { assert_eq!("⠭⠔⠝", braille, "Grade1"); return Ok( () ); } + + #[test] + fn french_remove_optional_group_indicators() { + assert_eq!(remove_optional_group_indicators("bLxe+"), "Lx+"); + assert_eq!(remove_optional_group_indicators("bLxeLy"), "oLxcLy"); + assert_eq!(remove_optional_group_indicators("bLxbe+NeLz"), "oLx+NcLz"); + // inner e is followed by outer e, then L — resolve outer pair first + assert_eq!(remove_optional_group_indicators("bbLx+NeeLy"), "ooLx+NccLy"); + // adjacent pairs: second is removed first, then first sees L after it + assert_eq!( + remove_optional_group_indicators("bL⠈N⠹⠜L⠁ebL⠈N⠩⠜L⠃e"), + "oL⠈N⠹⠜L⠁cL⠈N⠩⠜L⠃" + ); + } } diff --git a/tests/braille/French/french.rs b/tests/braille/French/french.rs index d0c073128..a799b8ed7 100644 --- a/tests/braille/French/french.rs +++ b/tests/braille/French/french.rs @@ -6,6 +6,35 @@ use crate::common::*; use anyhow::Result; +#[test] +fn binomial_nk_01() -> Result<()> { + let expr= r#"(nk)"#; + test_braille("French", expr, "⠘⠦⠝⠠⠜⠅⠘⠴")?; + return Ok(()); +} + +#[test] +fn binomial_5_2_02() -> Result<()> { + let expr= r#"(52)"#; + test_braille("French", expr, "⠘⠦⠱⠠⠜⠣⠘⠴")?; + return Ok(()); +} + +#[test] +fn binomial_symmetry_03() -> Result<()> { + let expr= r#"(nk)=(nn−k)"#; + test_braille("French", expr, "⠘⠦⠝⠠⠜⠅⠘⠴⠶⠘⠦⠝⠠⠜⠝⠤⠅⠘⠴")?; + return Ok(()); +} + +#[test] +fn binomial_pascal_04() -> Result<()> { + let expr= r#"(nk)=(n−1k−1)+(n−1k)"#; + test_braille("French", expr, "⠘⠦⠝⠠⠜⠅⠘⠴⠶⠘⠦⠝⠤⠡⠠⠜⠅⠤⠡⠘⠴⠖⠘⠦⠝⠤⠡⠠⠜⠅⠘⠴")?; + return Ok(()); +} + + #[test] fn set_of_elements_6_4_01() -> Result<()> { let expr= r#"E={a,b,c,d}"#; @@ -246,14 +275,18 @@ fn sub_mixt_0_9_4_02() -> Result<()> { #[test] fn choose_9_4_03() -> Result<()> { - let expr= r#"∁mp=∁mm−p"#; + let expr= r#"Cmp=Cmm−p"#; test_braille("French", expr, "⠨⠉⠢⠍⠈⠏⠶⠨⠉⠢⠍⠈⠰⠍⠤⠏⠆")?; return Ok(()); } #[test] fn sub_sup_mixed_formulaq_9_4_04() -> Result<()> { - let expr= r#"am+n′p+q"#; + let expr= r#" + a′' + m+n + p+q + "#; test_braille("French", expr, "⠁⠄⠄⠢⠰⠍⠖⠝⠆⠈⠰⠏⠖⠟⠆")?; return Ok(()); } @@ -273,7 +306,7 @@ fn left_sup_9_5_01() -> Result<()> { } #[test] -fn lef_sub_9_5_02() -> Result<()> { +fn left_sub_9_5_02() -> Result<()> { let expr= r#"2w"#; test_braille("French", expr, "⠢⠣⠺")?; return Ok(()); @@ -281,7 +314,7 @@ fn lef_sub_9_5_02() -> Result<()> { #[test] fn left_right_sup_9_5_03() -> Result<()> { - let expr= r#"AtBt"#; + let expr: &str= r#"AtBt"#; test_braille("French", expr, "⠰⠈⠞⠨⠁⠆⠰⠈⠞⠨⠃⠆")?; return Ok(()); } @@ -336,8 +369,8 @@ fn square_formula_3_10_05() -> Result<()> { #[test] fn function_from_r_to_r_11_01() -> Result<()> { - let expr= r#"f:ℝ→ℝ,x↦y=1x"#; - test_braille("French", expr, "⠒⠨⠨⠗⠸⠱⠨⠨⠗⠭⠐⠱⠽⠶⠡⠌⠭")?; + let expr= r#"f:ℝ→ℝ,x↦y=1x"#; + test_braille("French", expr, "⠋⠒⠨⠨⠗⠸⠱⠨⠨⠗⠭⠐⠱⠽⠶⠡⠌⠭")?; return Ok(()); } @@ -486,7 +519,7 @@ fn limit_x_tends_1_14_2_01_corrected() -> Result<()> { #[test] fn limit_x_tends_and_x_lt_1_14_2_02_corrected_1() -> Result<()> { - let expr= r#"limx→4x>4f(x)=−∞"#; + let expr= r#"limx→4x>4f(x)=−∞"#; test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠹⠂⠭⠐⠜⠹⠆⠋⠦⠭⠴⠶⠤⠘⠉")?; return Ok(()); } @@ -554,14 +587,14 @@ fn integral_from_to_2_14_4_03() -> Result<()> { #[test] fn integral_over_curve_14_4_04() -> Result<()> { - let expr= r#"∫(C)f(x)dx"#; + let expr= r#"∫(C)f(x)dx"#; test_braille("French", expr, "⠯⠢⠦⠨⠉⠴⠋⠦⠭⠴⠙⠭")?; return Ok(()); } #[test] fn integral_double_over_surface_14_4_05() -> Result<()> { - let expr= r#"∯Sf(x,y)dxdy"#; + let expr= r#"∯Sf(x,y)dxdy"#; test_braille("French", expr, "⠨⠯⠯⠢⠨⠎⠋⠦⠭⠂⠽⠴⠙⠭⠙⠽")?; return Ok(()); } @@ -603,7 +636,8 @@ fn cos_of_sum_2_14_6_01b() -> Result<()> { #[test] fn cosh_sinh_14_7_01() -> Result<()> { let expr= r#"cosh2x−sinh2x=1"#; - test_braille("French", expr, "⠉⠓⠈⠣⠰⠭⠆⠤⠎⠓⠈⠣⠰⠭⠆⠶⠡")?; + test_braille_prefs("French", vec![("French_UseShortForm", "true")], expr, "⠉⠓⠈⠣⠰⠭⠆⠤⠎⠓⠈⠣⠰⠭⠆⠶⠡")?; + return Ok(()); } @@ -616,7 +650,7 @@ fn exists_element_of_15_01() -> Result<()> { #[test] fn set_formula_15_02() -> Result<()> { - let expr= r#"∀ε>0∃η>0:|x2−x1|≤η⇒|f(x2)−f(x1)|≤ε"#; + let expr= r#"∀ε>0∃η>0:|x2−x1|≤η⇒|f(x2)−f(x1)|≤ε"#; test_braille("French", expr, "⠸⠌⠘⠑⠐⠜⠼⠀⠸⠡⠘⠓⠐⠜⠼⠒⠀⠿⠭⠢⠣⠤⠭⠢⠡⠿⠘⠣⠘⠓⠀⠒⠂⠀⠿⠋⠦⠭⠢⠣⠴⠤⠋⠦⠭⠢⠡⠴⠿⠘⠣⠘⠑")?; return Ok(()); } diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 13dfbeb45..618eeb294 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -142,6 +142,7 @@ pub fn test_braille(code: &str, mathml: &str, braille: &str) -> Result<()> { // log::debug!("\nsetting Language"); match code { "Vietnam" => set_preference("Language", "vi").unwrap(), + "French" => set_preference("Language", "fr").unwrap(), "CMU" => set_preference("Language", "es").unwrap(), _ => set_preference("Language", "en").unwrap(), } @@ -169,6 +170,7 @@ pub fn test_braille_prefs(code: &str, test_prefs: Vec<(&str, &str)>, mathml: &st // log::debug!("\nsetting Language"); match code { "Vietnam" => set_preference("Language", "vi").unwrap(), + "French" => set_preference("Language", "fr").unwrap(), "CMU" => set_preference("Language", "es").unwrap(), _ => set_preference("Language", "en").unwrap(), } @@ -273,6 +275,7 @@ pub fn test_from_braille(code: &str, mathml: &str, braille: &str) -> Result<()> // log::debug!("\nsetting Language"); match code { "Vietnam" => set_preference("Language", "vi").unwrap(), + "French" => set_preference("Language", "fr").unwrap(), "CMU" => set_preference("Language", "es").unwrap(), _ => set_preference("Language", "en").unwrap(), } From f9ba42ab0235810afd6c282b3501e4eb2d9e4caf Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 25 Jun 2026 14:41:50 -0700 Subject: [PATCH 010/120] pass through braille chars as is --- src/speech.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/speech.rs b/src/speech.rs index 2545d308a..e97da7e0c 100644 --- a/src/speech.rs +++ b/src/speech.rs @@ -2822,18 +2822,21 @@ impl<'c, 's:'c, 'r, 'm:'c> SpeechRulesWithContext<'c, 's,'m> { unicode = rules.unicode_full.borrow(); replacements = unicode.get( &ch_as_u32 ); if replacements.is_none() { - self.translate_count = 0; // not in loop - // debug!("*** Did not find unicode {} for char '{}'/{:#06x}", rules.name, ch, ch_as_u32); - if rules.translate_single_chars_only || ch.is_ascii() { // speech or if braille, avoid loop (ASCII remains ASCII if not found) - return Ok(self.escape_string_for_safety(String::from(ch))); - } else { // braille -- must turn into braille dots - // Emulate what NVDA does: generate (including single quotes) '\xhhhh' or '\yhhhhhh' - let ch_as_int = ch as u32; - let prefix_indicator = if ch_as_int < 1<<16 {'x'} else {'y'}; - return self.replace_chars( &format!("'\\{prefix_indicator}{:06x}'", ch_as_int), mathml); + self.translate_count = 0; // not in loop + // debug!("*** Did not find unicode {} for char '{}'/{:#06x}", rules.name, ch, ch_as_u32); + if rules.translate_single_chars_only || ch.is_ascii() { // speech or if braille, avoid loop (ASCII remains ASCII if not found) + return Ok(self.escape_string_for_safety(String::from(ch))); + } else { + let ch_as_int = ch as u32; + if '\u{2800}' <= ch && ch <= '\u{28ff}' { // braille -- leave as braille + return Ok(self.escape_string_for_safety(String::from(ch))); + } else { // Emulate what NVDA does: generate (including single quotes) '\xhhhh' or '\yhhhhhh' + let prefix_indicator = if ch_as_int < 1<<16 {'x'} else {'y'}; + return self.replace_chars( &format!("'\\{prefix_indicator}{:06x}'", ch_as_int), mathml); + } + } } - } - }; + }; // map across all the parts of the replacement, collect them up into a Vec, and then concat them together let result = replacements.unwrap() From 52d91c23930daaf49f913895668bd6e2e869860f Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Wed, 5 Aug 2026 11:58:32 -0700 Subject: [PATCH 011/120] UEB has a rule for some pseudo-script chars. The match for that rule was overly broad, so I removed scripted letters and numbers. That rule made its way into other braille codes, so I removed them everywhere. All tests pass. --- Rules/Braille/CMU/CMU_Rules.yaml | 30 ++++++++++++------------ Rules/Braille/Swedish/Swedish_Rules.yaml | 2 +- Rules/Braille/UEB/UEB_Rules.yaml | 2 +- Rules/Braille/Vietnam/Vietnam_Rules.yaml | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Rules/Braille/CMU/CMU_Rules.yaml b/Rules/Braille/CMU/CMU_Rules.yaml index fb53fbeb8..3b5c59a40 100644 --- a/Rules/Braille/CMU/CMU_Rules.yaml +++ b/Rules/Braille/CMU/CMU_Rules.yaml @@ -598,7 +598,7 @@ - name: single-char-exceptions # 4.3.1 tag: msup - match: "*[2][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']" + match: "*[2][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']" replace: - x: "*" - test: @@ -609,7 +609,7 @@ - name: single-char-exceptions # 4.3.1 tag: msubsup - match: "*[3][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']" + match: "*[3][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']" replace: - x: "*[1]" - x: "*[3]" @@ -633,8 +633,8 @@ name: number-char-exceptions # 4.3.1 (FIX: degree doesn't use dot-3 for chemistry or temp/angles/etc) tag: [msub, msup] match: - - "*[2][translate(., \"'*`+-∞ª°²³´¹º‘’“”‟‵‶‷\",'')=''] or" - - "*[2][self::m:mrow and count(*)=2 and *[1][self::m:mn] and *[2][self::m:mo][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']]" + - "*[2][translate(., \"'*`+-∞´°‘’“”‟‵‶‷\",'')=''] or" + - "*[2][self::m:mrow and count(*)=2 and *[1][self::m:mn] and *[2][self::m:mo][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']]" replace: - x: "*[1]" - test: @@ -779,7 +779,7 @@ replace: - x: "*[1]" - test: # special case superscript postscripts that preceed the prescripts - if: "$Postscripts and not($Postscripts[2][self::m:none]) and $Postscripts[2][translate(., \"′″‴⁗'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']" + if: "$Postscripts and not($Postscripts[2][self::m:none]) and $Postscripts[2][translate(., \"′″‴⁗'*`+-´°‘’“”‟‵‶‷\",'')='']" then: - x: "$Postscripts[2]" - test: @@ -805,7 +805,7 @@ # degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here if: "not($Prescripts[1][.='°'] and *[1][self::m:mn or @data-chem-element])" then: [t: "⠄"] - - else_if: "$Prescripts[1][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']" + - else_if: "$Prescripts[1][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']" then: - x: "$Prescripts[1]" - test: @@ -835,7 +835,7 @@ # degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here if: "not($Prescripts[2][.='°'] and *[1][self::m:mn or @data-chem-element])" then: [t: "⠄"] - - else_if: "$Prescripts[2][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']" + - else_if: "$Prescripts[2][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']" then: - x: "$Prescripts[2]" - test: @@ -868,7 +868,7 @@ # degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here if: "not($Prescripts[3][.='°'] and *[1][self::m:mn or @data-chem-element])" then: [t: "⠄"] - - else_if: "$Prescripts[3][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']" + - else_if: "$Prescripts[3][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']" then: - x: "$Prescripts[3]" - test: @@ -898,7 +898,7 @@ # degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here if: "not($Prescripts[4][.='°'] and *[1][self::m:mn or @data-chem-element])" then: [t: "⠄"] - - else_if: "$Prescripts[4][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']" + - else_if: "$Prescripts[4][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']" then: - x: "$Prescripts[4]" - test: @@ -935,7 +935,7 @@ # degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here if: "not($Postscripts[1][.='°'] and *[1][self::m:mn or @data-chem-element])" then: [t: "⠄"] - - else_if: "$Postscripts[1][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']" + - else_if: "$Postscripts[1][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']" then: - x: "$Postscripts[1]" - test: @@ -950,7 +950,7 @@ else: - x: "$Postscripts[1]" - test: - if: "not($Postscripts[2][self::m:none]) and $Postscripts[2][translate(., \"′″‴⁗'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')!='']" + if: "not($Postscripts[2][self::m:none]) and $Postscripts[2][translate(., \"′″‴⁗'*`+-´°‘’“”‟‵‶‷\",'')!='']" then: - t: "⠡" - test: @@ -991,7 +991,7 @@ # degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here if: "not($Postscripts[3][.='°'] and *[1][self::m:mn or @data-chem-element])" then: [t: "⠄"] - - else_if: "$Postscripts[3][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']" + - else_if: "$Postscripts[3][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']" then: - x: "$Postscripts[3]" - test: @@ -1021,7 +1021,7 @@ # degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here if: "not($Postscripts[4][.='°'] and *[1][self::m:mn or @data-chem-element])" then: [t: "⠄"] - - else_if: "$Postscripts[4][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']" + - else_if: "$Postscripts[4][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']" then: - x: "$Postscripts[4]" - test: @@ -1054,7 +1054,7 @@ # degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here if: "not($Postscripts[5][.='°'] and *[1][self::m:mn or @data-chem-element])" then: [t: "⠄"] - - else_if: "$Postscripts[5][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']" + - else_if: "$Postscripts[5][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']" then: - x: "$Postscripts[5]" - test: @@ -1084,7 +1084,7 @@ # degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here if: "not($Postscripts[6][.='°'] and *[1][self::m:mn or @data-chem-element])" then: [t: "⠄"] - - else_if: "$Postscripts[6][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']" + - else_if: "$Postscripts[6][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']" then: - x: "$Postscripts[6]" - test: diff --git a/Rules/Braille/Swedish/Swedish_Rules.yaml b/Rules/Braille/Swedish/Swedish_Rules.yaml index 028574914..7a8504574 100644 --- a/Rules/Braille/Swedish/Swedish_Rules.yaml +++ b/Rules/Braille/Swedish/Swedish_Rules.yaml @@ -348,7 +348,7 @@ - name: single-char-exceptions # RUEB 3.11 -- ° and various prime/minute/seconds aren't treated as if superscripts tag: msup - match: "*[2][translate(., \"'*`ª°²³´¹º‘’“”‟′″‴‵‶‷⁗\",'')='']" + match: "*[2][translate(., \"'*`´°‘’“”‟′″‴‵‶‷⁗\",'')='']" replace: - test: if: "NeedsToBeGrouped(*[1], 'Swedish', true())" diff --git a/Rules/Braille/UEB/UEB_Rules.yaml b/Rules/Braille/UEB/UEB_Rules.yaml index b9e9acb79..8e3491c80 100644 --- a/Rules/Braille/UEB/UEB_Rules.yaml +++ b/Rules/Braille/UEB/UEB_Rules.yaml @@ -340,7 +340,7 @@ - name: single-char-exceptions # RUEB 3.11 -- ° and various prime/minute/seconds aren't treated as if superscripts tag: [msup] - match: "*[2][translate(., \"'*`ª°²³´¹º‘’“”‟′″‴‵‶‷⁗\",'')='']" + match: "*[2][translate(., \"'*`´°‘’“”‟′″‴‵‶‷⁗\",'')='']" replace: - test: if: "NeedsToBeGrouped(*[1], 'UEB', true())" diff --git a/Rules/Braille/Vietnam/Vietnam_Rules.yaml b/Rules/Braille/Vietnam/Vietnam_Rules.yaml index 88947d646..2b43067be 100644 --- a/Rules/Braille/Vietnam/Vietnam_Rules.yaml +++ b/Rules/Braille/Vietnam/Vietnam_Rules.yaml @@ -437,7 +437,7 @@ - name: single-char-exceptions # RUEB 3.11 -- ° and various prime/minute/seconds aren't treated as if superscripts tag: msup - match: "*[2][translate(., \"'*`ª°²³´¹º‘’“”‟′″‴‵‶‷⁗\",'')='']" + match: "*[2][translate(., \"'*`´°‘’“”‟′″‴‵‶‷⁗\",'')='']" replace: [x: "*"] - From baf7de6c4c4cb70e53eb3b5edc849c781e59bb76 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Wed, 5 Aug 2026 12:00:12 -0700 Subject: [PATCH 012/120] =?UTF-8?q?Added=20new=20UEB=20chars.=20Many=20of?= =?UTF-8?q?=20these=20are=20accented=20letters.=20I=20added=20'A'=20(for?= =?UTF-8?q?=20accented=20letters)=20so=20that=20the=20pattern=20for=20them?= =?UTF-8?q?=20looks=20like:=20```=20=20-=20"=C3=91":=20[t:=20"CA=E2=A0=98?= =?UTF-8?q?=E2=A0=BBL=E2=A0=9D"]=20=20=20=20=20=20=20=20=20=20=20=20=20#?= =?UTF-8?q?=200x00D1=20```?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a few tests. --- Rules/Braille/UEB/unicode-full.yaml | 146 ++++++++++++++++++++++++++- Rules/Braille/UEB/unicode.yaml | 6 +- src/braille.rs | 149 +++++++++++++++++++++++++--- tests/braille/UEB/other.rs | 28 ++++++ 4 files changed, 308 insertions(+), 21 deletions(-) diff --git a/Rules/Braille/UEB/unicode-full.yaml b/Rules/Braille/UEB/unicode-full.yaml index df7bf2274..48bc20ead 100644 --- a/Rules/Braille/UEB/unicode-full.yaml +++ b/Rules/Braille/UEB/unicode-full.yaml @@ -44,8 +44,150 @@ - "⅜": [tc: "#N⠉N⠌N⠓"] # 0x215C (Vulgar Fraction Three Eighths) - "⅝": [tc: "#N⠑N⠌N⠓"] # 0x215D (Vulgar Fraction Five Eighths) - "⅞": [tc: "#N⠛N⠌N⠓"] # 0x215E (Vulgar Fraction Seven Eighths) - - "↉": [tc: "#N⠚N⠌N⠑"] # 0x2189 (Vulgar Fraction Zero Thirds) - + - "↉": [tc: "#N⠚N⠌N⠉"] # 0x2189 (Vulgar Fraction Zero Thirds) + + # --- UEB_Additions1 (ordered by Unicode; Å/–/≣ remain in unicode.yaml) --- + - "`": [t: "⠨⠡"] # 0x0060 + - "¡": [t: "⠘⠰⠖"] # 0x00A1 + - "§": [t: "⠘⠎"] # 0x00A7 + - "­": [t: "⠤"] # 0x00AD (soft hyphen) + - "®": [t: "⠘⠗"] # 0x00AE + - "¯": [t: "⠱"] # 0x00AF + - "²": # 0x00B2 + - test: + if: "(parent::m:msup or parent::m:msubsup) and not(following-sibling::*)" + then: [t: "⠼⠃"] + else: [t: "1⠔⠼⠃"] + - "³": # 0x00B3 + - test: + if: "(parent::m:msup or parent::m:msubsup) and not(following-sibling::*)" + then: [t: "⠼⠉"] + else: [t: "1⠔⠼⠉"] + - "¶": [t: "⠘⠏"] # 0x00B6 + - "¹": # 0x00B9 + - test: + if: "(parent::m:msup or parent::m:msubsup) and not(following-sibling::*)" + then: [t: "⠼⠁"] + else: [t: "1⠔⠼⠁"] + - "¿": [t: "⠘⠰⠦"] # 0x00BF + - "À": [t: "CA⠘⠡L⠁"] # 0x00C0 + - "Á": [t: "CA⠘⠌L⠁"] # 0x00C1 + - "Â": [t: "CA⠘⠩L⠁"] # 0x00C2 + - "Ã": [t: "CA⠘⠻L⠁"] # 0x00C3 + - "Ä": [t: "CA⠘⠒L⠁"] # 0x00C4 + - "Æ": [t: "C⠁⠠⠘⠖⠑"] # 0x00C6 + - "Ç": [t: "CA⠘⠯L⠉"] # 0x00C7 + - "È": [t: "CA⠘⠡L⠑"] # 0x00C8 + - "É": [t: "CA⠘⠌L⠑"] # 0x00C9 + - "Ê": [t: "CA⠘⠩L⠑"] # 0x00CA + - "Ë": [t: "CA⠘⠒L⠑"] # 0x00CB + - "Ì": [t: "CA⠘⠡L⠊"] # 0x00CC + - "Í": [t: "CA⠘⠌L⠊"] # 0x00CD + - "Î": [t: "CA⠘⠩L⠊"] # 0x00CE + - "Ï": [t: "CA⠘⠒L⠊"] # 0x00CF + - "Ð": [t: "C⠼⠫"] # 0x00D0 + - "Ñ": [t: "CA⠘⠻L⠝"] # 0x00D1 + - "Ò": [t: "CA⠘⠡L⠕"] # 0x00D2 + - "Ó": [t: "CA⠘⠌L⠕"] # 0x00D3 + - "Ô": [t: "CA⠘⠩L⠕"] # 0x00D4 + - "Õ": [t: "CA⠘⠻L⠕"] # 0x00D5 + - "Ö": [t: "CA⠘⠒L⠕"] # 0x00D6 + - "Ø": [t: "CA⠈⠡L⠕"] # 0x00D8 + - "Ù": [t: "CA⠘⠡L⠥"] # 0x00D9 + - "Ú": [t: "CA⠘⠌L⠥"] # 0x00DA + - "Û": [t: "CA⠘⠩L⠥"] # 0x00DB + - "Ü": [t: "CA⠘⠒L⠥"] # 0x00DC + - "Ý": [t: "CA⠘⠌L⠽"] # 0x00DD + - "Þ": [t: "C⠼⠮"] # 0x00DE + - "ß": [t: "⠨⠮"] # 0x00DF + - "à": [t: "A⠘⠡L⠁"] # 0x00E0 + - "á": [t: "A⠘⠌L⠁"] # 0x00E1 + - "â": [t: "A⠘⠩L⠁"] # 0x00E2 + - "ã": [t: "A⠘⠻L⠁"] # 0x00E3 + - "ä": [t: "A⠘⠒L⠁"] # 0x00E4 + - "å": [t: "A⠘⠫L⠁"] # 0x00E5 + - "æ": [t: "⠁⠘⠖⠑"] # 0x00E6 + - "ç": [t: "A⠘⠯L⠉"] # 0x00E7 + - "è": [t: "A⠘⠡L⠑"] # 0x00E8 + - "é": [t: "A⠘⠌L⠑"] # 0x00E9 + - "ê": [t: "A⠘⠩L⠑"] # 0x00EA + - "ë": [t: "A⠘⠒L⠑"] # 0x00EB + - "ì": [t: "A⠘⠡L⠊"] # 0x00EC + - "í": [t: "A⠘⠌L⠊"] # 0x00ED + - "î": [t: "A⠘⠩L⠊"] # 0x00EE + - "ï": [t: "A⠘⠒L⠊"] # 0x00EF + - "ð": [t: "⠼⠫"] # 0x00F0 + - "ñ": [t: "A⠘⠻L⠝"] # 0x00F1 + - "ò": [t: "A⠘⠡L⠕"] # 0x00F2 + - "ó": [t: "A⠘⠌L⠕"] # 0x00F3 + - "ô": [t: "A⠘⠩L⠕"] # 0x00F4 + - "õ": [t: "A⠘⠻L⠕"] # 0x00F5 + - "ö": [t: "A⠘⠒L⠕"] # 0x00F6 + - "ø": [t: "A⠈⠡L⠕"] # 0x00F8 + - "ù": [t: "A⠘⠡L⠥"] # 0x00F9 + - "ú": [t: "A⠘⠌L⠥"] # 0x00FA + - "û": [t: "A⠘⠩L⠥"] # 0x00FB + - "ü": [t: "A⠘⠒L⠥"] # 0x00FC + - "ý": [t: "A⠘⠌L⠽"] # 0x00FD + - "þ": [t: "⠼⠮"] # 0x00FE + - "ÿ": [t: "A⠘⠒L⠽"] # 0x00FF + - "Ÿ": [t: "CA⠘⠒L⠽"] # 0x0178 + - "ʹ": [t: "⠶"] # 0x02B9 + - "ʺ": [t: "⠶⠶"] # 0x02BA + - "˜": [t: "⠈⠔"] # 0x02DC + - "ẞ": [t: "C⠨⠮"] # 0x1E9E + - "‑": [t: "⠤"] # 0x2011 (non-breaking hyphen) + - "‒": [t: "⠤"] # 0x2012 (figure dash) + - "—": [t: "P⠠⠤"] # 0x2014 (em dash) + - "•": [t: "⠸⠲"] # 0x2022 + - "․": [t: "⠲"] # 0x2024 + - "‥": [t: "⠲⠲"] # 0x2025 + - "‼": [t: "⠖⠖"] # 0x203C + - "‾": [t: "⠱"] # 0x203E + - "⁇": [t: "⠦⠦"] # 0x2047 + - "⁈": [t: "⠦⠖"] # 0x2048 + - "⁉": [t: "⠖⠦"] # 0x2049 + - "⁗": [t: "⠶⠶⠶⠶"] # 0x2057 + - "⁰": # 0x2070 + - test: + if: "(parent::m:msup or parent::m:msubsup) and not(following-sibling::*)" + then: [t: "⠼⠚"] + else: [t: "1⠔⠼⠚"] + - "ⁱ": # 0x2071 + - test: + if: "(parent::m:msup or parent::m:msubsup) and not(following-sibling::*)" + then: [t: "⠊"] + else: [t: "1⠔⠊"] + - "⁴-⁹": # 0x2074 - 0x2079 + - test: + if: "(parent::m:msup or parent::m:msubsup) and not(following-sibling::*)" + then: [t: "⠼"] + else: [t: "1⠔⠼"] + - spell: "translate('.', '⁴⁵⁶⁷⁸⁹', '⠙⠑⠋⠛⠓⠊')" + - "⁺-⁾": # 0x207A - 0x207E + - test: + if: "(parent::m:msup or parent::m:msubsup) and not(following-sibling::*)" + then: [t: "⠐"] + else: [t: "1⠔⠐"] + - spell: "translate('.', '⁺⁻⁼⁽⁾', '⠖⠤⠶⠣⠜')" + - "ⁿ": # 0x207F + - test: + if: "(parent::m:msup or parent::m:msubsup) and not(following-sibling::*)" + then: [t: "⠝"] + else: [t: "1⠔⠝"] + - "₀-₉": # 0x2080 - 0x2089 + - test: + if: "(parent::m:msub or parent::m:msubsup) and count(preceding-sibling::*) = 1" + then: [t: "⠼"] + else: [t: "1⠢⠼"] + - spell: "translate('.', '₀₁₂₃₄₅₆₇₈₉', '⠚⠁⠃⠉⠙⠑⠋⠛⠓⠊')" + - "₊-₎": # 0x208A - 0x208E + - test: + if: "(parent::m:msub or parent::m:msubsup) and count(preceding-sibling::*) = 1" + then: [t: "⠐"] + else: [t: "1⠢⠐"] + - spell: "translate('.', '₊₋₌₍₎', '⠖⠤⠶⠣⠜')" + - "™": [t: "⠘⠞"] # 0x2122 - "ℊ": [tc: "TL⠛⠁"] # 0x210a (Script Small G) - "ℋ": [tc: "TCL⠓⠁"] # 0x210b (Script Capital H) diff --git a/Rules/Braille/UEB/unicode.yaml b/Rules/Braille/UEB/unicode.yaml index 3256d59c5..e1b513bc8 100644 --- a/Rules/Braille/UEB/unicode.yaml +++ b/Rules/Braille/UEB/unicode.yaml @@ -103,7 +103,7 @@ - "~": [t: "⠈⠔"] # 0x007E (Tilde (swung dash)) - "¢": [t: "⠈⠉"] # 0x00A2 (Cents sign) - "‐": [t: "⠤"] # 0x2010 (Hyphen) - - "–": [t: "⠠⠤"] # 0x2013 (En Dash) + - "–": [t: "P⠠⠤"] # 0x2013 (En Dash) - "―": [t: "⠐⠠⠤"] # 0x2015 (Horizontal Bar) - "‖": [t: "⠸⠳"] # 0x2016 - "€": [t: "⠈⠑"] # 0x20AC (Euro sign) @@ -123,8 +123,8 @@ - "‴": [t: "1⠶⠶⠶"] # 0x2034 (Triple prime sign) - "℃": [t: "⠘⠚⠠⠉"] # 0x2103 (Degrees Celsius) - "℉": [t: "⠘⠚⠠⠋"] # 0x2109 (Degrees Fahrenheit) - - "Å": [t: "⠠⠘⠫⠁"] # 0x212B (Angstrom symbol) - - "Å": [t: "⠠⠘⠫⠁"] # 0x00C5 (WIRIS uses this for Angstrom) + - "Å": [t: "CA⠘⠫L⠁"] # 0x212B (Angstrom symbol) + - "Å": [t: "CA⠘⠫L⠁"] # 0x00C5 (WIRIS uses this for Angstrom) - "∀": [t: "⠘⠁"] # 0x2200 (For all) - "∂": [t: "⠈⠙"] # 0x2202 (Partial differential) - "∃": [t: "⠘⠢"] # 0x2203 (There exists) diff --git a/src/braille.rs b/src/braille.rs index 2b7b5b34a..54fd1a45c 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -896,6 +896,7 @@ static UEB_INDICATOR_REPLACEMENTS: phf::Map<&str, &str> = phf_map! { "1" => "⠰", // Grade 1 symbol "𝟙" => "⠰⠰", // Grade 1 word "L" => "", // Letter left in to assist in locating letters + "A" => "", // Letter accent indicator -- should be followed by accent cells and then "L" for char "D" => "XXX", // German (Deutsche) -- from prefs "G" => "⠨", // Greek "V" => "⠨⠈", // Greek Variants @@ -934,6 +935,24 @@ fn is_letter_number(ch: char) -> bool { matches!(ch, '⠁' | '⠃' | '⠉' | '⠙' | '⠑' | '⠋' | '⠛' | '⠓' | '⠊' | '⠚') } +fn is_braille_char(ch: char) -> bool { + ('\u{2800}'..='\u{28ff}').contains(&ch) +} + +/// After an 'A' accent indicator, skip accent braille cells and return the index of the following 'L'. +/// Returns None if the accented-letter pattern is malformed. +fn index_after_accent_to_l(chars: &[char], after_a: usize) -> Option { + let mut j = after_a; + while j < chars.len() && is_braille_char(chars[j]) { + j += 1; + } + if j < chars.len() && chars[j] == 'L' { + Some(j) + } else { + None + } +} + static SHORT_FORMS: phf::Set<&str> = phf_set! { "L⠁L⠃", "L⠁L⠃L⠧", "L⠁L⠉", "L⠁L⠉L⠗", "L⠁L⠋", "L⠁L⠋L⠝", "L⠁L⠋L⠺", "L⠁L⠛", "L⠁L⠛L⠌", "L⠁L⠇", @@ -951,7 +970,7 @@ static SHORT_FORMS: phf::Set<&str> = phf_set! { }; fn is_letter_prefix(ch: char) -> bool { - matches!(ch, 'B' | 'I' | '𝔹' | 'S' | 'T' | 'D' | 'C' | '𝐶' | '𝑐') + matches!(ch, 'B' | 'I' | '𝔹' | 'S' | 'T' | 'D' | 'C' | '𝐶' | '𝑐' | 'A') } // Trim braille spaces before and after braille indicators @@ -959,7 +978,7 @@ fn is_letter_prefix(ch: char) -> bool { // Note: fraction over is not listed due to example 42(4) which shows a space before the "/" // static ref REMOVE_SPACE_BEFORE_BRAILLE_INDICATORS: Regex = // Regex::new(r"(⠄⠄⠄|⠤⠤⠤)W+([⠼⠸⠪])").unwrap(); -static REPLACE_INDICATORS: LazyLock = LazyLock::new(|| Regex::new(r"([1𝟙SB𝔹TIREDGVHP𝐶𝑐CLMNW𝐖swe,.-—―#ocb])").unwrap()); +static REPLACE_INDICATORS: LazyLock = LazyLock::new(|| Regex::new(r"([1𝟙SB𝔹TIREDGVHP𝐶𝑐CLAMNW𝐖swe,.-—―#ocb])").unwrap()); static COLLAPSE_SPACES: LazyLock = LazyLock::new(|| Regex::new(r"⠀⠀+").unwrap()); /// Transcriber-defined typeforms pulled from prefs (their braille is configurable), used by the @@ -1341,6 +1360,16 @@ fn capitals_to_word_mode(braille: &str) -> String { // Greek letters are a bit exceptional in that the pattern is "CGLx" -- bump 'i' next_non_cap += 1; } + if chars[next_non_cap] == 'A' { + // Accented capital: CA...Lx + match index_after_accent_to_l(&chars, next_non_cap + 1) { + Some(i_l) => next_non_cap = i_l, + None => { + error!("capitals_to_word_mode: internal error: didn't find L after CA in '{}'.", + chars[i..].iter().take(8).collect::()); + } + } + } if chars[next_non_cap] != 'L' { error!("capitals_to_word_mode: internal error: didn't find L after C in '{}'.", chars[i..next_non_cap+2].iter().collect::().as_str()); @@ -1348,6 +1377,25 @@ fn capitals_to_word_mode(braille: &str) -> String { let i_braille_char = next_non_cap + 2; result.push_str(String::from_iter(&chars[i..i_braille_char]).as_str()); i = i_braille_char; + } else if ch == 'A' { + // Lowercase accented letter: A...Lx + if is_word_mode { + result.push('e'); + is_word_mode = false; + } + match index_after_accent_to_l(&chars, i + 1) { + Some(i_l) => { + let i_braille_char = i_l + 2; + result.push_str(String::from_iter(&chars[i..i_braille_char]).as_str()); + i = i_braille_char; + } + None => { + error!("capitals_to_word_mode: internal error: didn't find L after A in '{}'.", + chars[i..].iter().take(8).collect::()); + result.push(ch); + i += 1; + } + } } else if ch == 'L' { // must be lowercase -- uppercase consumed above // assert!(LETTERS.contains(&unhighlight(chars[i+1]))); not true for other alphabets if is_word_mode { @@ -1562,15 +1610,23 @@ fn remove_unneeded_mode_changes(raw_braille: &str, start_mode: UEB_Mode, start_d match ch { 'L' => { // note: be aware of '#' case for Numeric because '1' might already be generated - // let prev_ch = if i > 1 {chars[i-1]} else {'1'}; // '1' -- anything beside ',' or '.' - // if duration == UEB_Duration::Symbol || - // ( ",. ".contains(prev_ch) && LETTER_NUMBERS.contains(&unhighlight(chars[i+1])) ) { - // result.push('1'); // need to retain grade 1 indicator (RUEB 6.5.2) - // } // let the default case handle pushing on the chars for the letter result.push(ch); i += 1; }, + 'A' => { + // Accented letter: keep A...Lx together so accent cells don't end G1 symbol mode + match index_after_accent_to_l(&chars, i + 1) { + Some(i_l) => { + result.extend(chars[i..i_l + 2].iter().copied()); // A + accents + L + letter + i = i_l + 2; + } + None => { + result.push(ch); + i += 1; + } + } + }, '1' | '𝟙' => { assert!(ch == '1' || duration != UEB_Duration::Symbol); // if '𝟙', should be Word or Passage duration // nothing to do -- let the default case handle the following chars @@ -1629,14 +1685,29 @@ fn remove_unneeded_mode_changes(raw_braille: &str, start_mode: UEB_Mode, start_d i += right_matched_chars.len(); }, 'C' => { - // Want 'C' before 'L'; Could be CC for word cap -- if so, eat it and move on + // Want 'C' before 'L' (or 'CA...L' / 'CGL'). Could be CC for word cap -- if so, eat it and move on // Note: guaranteed that there is a char after the 'C', so chars[i+1] is safe if chars[i+1] == 'C' { cap_word_mode = true; i += 1; } else { - let is_greek = chars[i+1] == 'G'; - let (is_alone, right_matched_chars, n_letters) = stands_alone(&chars, if is_greek {i+2} else {i+1}); + let mut letter_idx = i + 1; + let is_greek = chars[letter_idx] == 'G'; + if is_greek { + letter_idx += 1; + } + if letter_idx < chars.len() && chars[letter_idx] == 'A' { + match index_after_accent_to_l(&chars, letter_idx + 1) { + Some(i_l) => letter_idx = i_l, + None => { + error!("Internal error: 'CA' without L at index={i} in '{raw_braille}'"); + result.push('C'); + i += 1; + continue; + } + } + } + let (is_alone, right_matched_chars, n_letters) = stands_alone(&chars, letter_idx); // GTM 1.2.1 says we only need to use G1 for single letters or sequences that are a shortform (e.g, "ab") if is_alone && (n_letters == 1 || is_short_form(&right_matched_chars[..2*n_letters])) { // debug!(" is_alone -- pushing '1'"); @@ -1649,12 +1720,33 @@ fn remove_unneeded_mode_changes(raw_braille: &str, start_mode: UEB_Mode, start_d result.push('C'); if is_greek { result.push('G'); - i += 1; } start_g2_letter = Some(i); - // debug!(" pushing 'C' + {:?}", right_matched_chars); + // right_matched starts at A or L (not including C/G) right_matched_chars.iter().for_each(|&ch| result.push(ch)); - i += 1 + right_matched_chars.len(); + i += 1 + if is_greek {1} else {0} + right_matched_chars.len(); + } + }, + 'A' => { + // Accented lowercase letter: A...Lx + match index_after_accent_to_l(&chars, i + 1) { + Some(i_l) => { + if start_g2_letter.is_none() { + start_g2_letter = Some(i); + } + let (is_alone, right_matched_chars, n_letters) = stands_alone(&chars, i_l); + if is_alone && (n_letters == 1 || is_short_form(&right_matched_chars[..2*n_letters])) { + result.push('1'); + mode = UEB_Mode::Grade1; + } + right_matched_chars.iter().for_each(|&ch| result.push(ch)); + i += right_matched_chars.len(); + } + None => { + error!("Internal error: 'A' without L at index={i} in '{raw_braille}'"); + result.push(ch); + i += 1; + } } }, '1' => { @@ -1747,8 +1839,21 @@ fn stands_alone(chars: &[char], i: usize) -> (bool, &[char], usize) { // we scan forward and check the conditions for "standing-alone" assert_eq!(chars[i], 'L', "'stands_alone' starts with non 'L'"); // debug!("stands_alone: i={}, chars: {:?}", i, chars); - if !left_side_stands_alone(&chars[0..i]) { - return (false, &chars[i..i+2], 0); + // Exclude accent prefix A... immediately before this L (part of the same letter) + let mut letter_start = i; + let mut left_end = i; + if left_end > 0 { + let mut j = left_end; + while j > 0 && is_braille_char(chars[j - 1]) { + j -= 1; + } + if j > 0 && chars[j - 1] == 'A' { + letter_start = j - 1; + left_end = j - 1; + } + } + if !left_side_stands_alone(&chars[0..left_end]) { + return (false, &chars[letter_start..i+2], 0); } let (mut is_alone, n_letters, n_right_matched) = right_side_stands_alone(&chars[i+2..]); @@ -1760,7 +1865,8 @@ fn stands_alone(chars: &[char], i: usize) -> (bool, &[char], usize) { is_alone = false; } } - return (is_alone, &chars[i..i+2+n_right_matched], n_letters); + // Include any A... prefix so callers emit the full accented letter + return (is_alone, &chars[letter_start..i+2+n_right_matched], n_letters); /// chars before 'L' fn left_side_stands_alone(chars: &[char]) -> bool { @@ -1804,6 +1910,17 @@ fn stands_alone(chars: &[char], i: usize) -> (bool, &[char], usize) { if !intervening_chars_mode && ch == 'L' { n_letters += 1; i += 1; // ignore 'Lx' and also ignore 'ox' + } else if !intervening_chars_mode && ch == 'A' { + // Accented letter A...Lx counts as one letter + match index_after_accent_to_l(chars, i + 1) { + Some(i_l) => { + n_letters += 1; + i = i_l + 1; // now at letter braille; loop end advances past it + } + None => { + return (false, n_letters, i); + } + } } else if ch == 'c' || ch == 'b' { i += 1; // ignore 'Lx' and also ignore 'ox' } else if is_right_intervening_char(ch) { diff --git a/tests/braille/UEB/other.rs b/tests/braille/UEB/other.rs index 0c60e27db..35c7cfcf0 100644 --- a/tests/braille/UEB/other.rs +++ b/tests/braille/UEB/other.rs @@ -199,3 +199,31 @@ fn caps_bug_295() -> Result<()> { return Ok(()); } + +#[test] +fn unicode_superscript_chars_in_and_out_of_script_position() -> Result<()> { + // Scripted: msup adds ⠔; unicode then-clause omits 1 and ⠔ so result matches non-scripted + let expr = "x¹"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠁")?; + let expr = "x¹"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠁")?; + let expr = "x⁺"; + test_braille("UEB", expr, "⠭⠰⠔⠐⠖")?; + let expr = "x⁺"; + test_braille("UEB", expr, "⠭⠰⠔⠐⠖")?; + return Ok(()); +} + +#[test] +fn unicode_subscript_chars_in_and_out_of_script_position() -> Result<()> { + let expr = "x₁"; + test_braille("UEB", expr, "⠭⠰⠢⠼⠁")?; + let expr = "x₁"; + test_braille("UEB", expr, "⠭⠰⠢⠼⠁")?; + let expr = "x₊"; + test_braille("UEB", expr, "⠭⠰⠢⠐⠖")?; + let expr = "x₊"; + test_braille("UEB", expr, "⠭⠰⠢⠐⠖")?; + return Ok(()); +} + From 916978c0416687a93e452f0a87cd2bcf9d06c711 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Wed, 5 Aug 2026 22:25:32 -0700 Subject: [PATCH 013/120] change small spaces from 'W' to '' --- Rules/Braille/Nemeth/unicode-full.yaml | 12 ++++++------ Rules/Braille/UEB/unicode-full.yaml | 13 ++++++------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/Rules/Braille/Nemeth/unicode-full.yaml b/Rules/Braille/Nemeth/unicode-full.yaml index 06831aec3..13245da4e 100644 --- a/Rules/Braille/Nemeth/unicode-full.yaml +++ b/Rules/Braille/Nemeth/unicode-full.yaml @@ -1047,21 +1047,21 @@ - " ": [t: "W"] # 0x2003 - " ": [t: "W"] # 0x2004 - " ": [t: "W"] # 0x2005 - - " ": [t: "W"] # 0x2006 + - " ": [t: ""] # 0x2006 (six-per-em) - " ": [t: "W"] # 0x2007 - " ": [t: "W"] # 0x2008 - - " ": [t: "W"] # 0x2009 - - " ": [t: "W"] # 0x200a - - "​": [t: "W"] # 0x200b + - " ": [t: ""] # 0x2009 (thin space) + - " ": [t: ""] # 0x200a (hair space) + - "​": [t: ""] # 0x200b (zero-width space) - "
": [t: "W"] # 0x2028 - "
": [t: "W"] # 0x2029 - - " ": [t: "W"] # 0x202f + - " ": [t: ""] # 0x202f (narrow no-break space) - " ": [t: "W"] # 0x205f - "₣": [t: "⠈⠋"] # 0x20A3 (French franc sign - "₦": [t: "⠈⠝"] # 0x2046 (Naira sign) - "₩": [t: "⠈⠺"] # 0x20A9 (Won sign) - "€": [t: "⠈⠑"] # 0x20AC (Euro sign) - - "": [t: "W"] # 0xfeff + - "": [t: ""] # 0xfeff (BOM / ZWNBSP) - "☆": [t: "⠫⠎"] # 0x2606 - "✕": [t: "⠈⠡"] # 0x2715 - "¼": [t: "⠹⠂⠌⠲⠼"] # 0xbc diff --git a/Rules/Braille/UEB/unicode-full.yaml b/Rules/Braille/UEB/unicode-full.yaml index 48bc20ead..750493ef8 100644 --- a/Rules/Braille/UEB/unicode-full.yaml +++ b/Rules/Braille/UEB/unicode-full.yaml @@ -6,18 +6,17 @@ - " ": [t: "W"] # 0x2003 - " ": [t: "W"] # 0x2004 - " ": [t: "W"] # 0x2005 - - " ": [t: "W"] # 0x2006 + - " ": [t: ""] # 0x2006 (six-per-em) - " ": [t: "W"] # 0x2007 - " ": [t: "W"] # 0x2008 - - " ": [t: "W"] # 0x2009 - - " ": [t: "W"] # 0x200a - - "​": [t: "W"] # 0x200b + - " ": [t: ""] # 0x2009 (thin space) + - " ": [t: ""] # 0x200a (hair space) + - "​": [t: ""] # 0x200b (zero-width space) - "
": [t: "W"] # 0x2028 - "
": [t: "W"] # 0x2029 - - " ": [t: "W"] # 0x202f + - " ": [t: ""] # 0x202f (narrow no-break space) - " ": [t: "W"] # 0x205f - - "": [t: "W"] # 0xfeff - + - "": [t: ""] # 0xfeff (BOM / ZWNBSP) - "ℏ": [tc: "⠈⠒⠓"] # t: "B"F (reduced Plank's constant) - "ⅆ": [tc: "⠙"] # 0x2146 From 6bddbda41b79ade585c90168dc1c2655e3877c4b Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 6 Aug 2026 14:33:20 -0700 Subject: [PATCH 014/120] add 'H' for Hebrew alphabet indicator --- src/braille.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/braille.rs b/src/braille.rs index 54fd1a45c..f3442a325 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -2390,13 +2390,14 @@ static FRENCH_INDICATOR_REPLACEMENTS: phf::Map<&str, &str> = phf_map! { // Keys match indicator chars emitted by French unicode.yaml, unicode-full.yaml, and French_Rules.yaml "S" => "XXX", // sans-serif -- from prefs "B" => "⠸", // bold - "𝔹" => "⠨", // blackboard -- from prefs + "𝔹" => "⠨", // blackboard "T" => "⠈", // script "I" => "⠸", // italic "1" => "⠀", // Grade 1 symbol (used for number followed by a letter) "L" => "", // Letter left in to assist in locating letters "D" => "XXX", // German (Deutsche) -- from prefs "G" => "⠘", // Greek + "H" => "⠘⠘", // Hebrew "C" => "⠨", // capital "𝐶" => "⠨", // capital that never should get whitespace in front (from chemical element) "N" => "", // number indicator @@ -2543,7 +2544,7 @@ fn letter_or_number_after_b( fn french_cleanup(_pref_manager: Ref, raw_braille: String) -> String { // FIX: need to implement this -- this is just a copy of the Vietnam code - static REPLACE_INDICATORS: LazyLock = LazyLock::new(|| Regex::new(r"([1SB𝔹TILDGC𝐶NWscocbe])").unwrap()); + static REPLACE_INDICATORS: LazyLock = LazyLock::new(|| Regex::new(r"([1SB𝔹TILDGHC𝐶NWscocbe])").unwrap()); debug!("french_cleanup: start={}", raw_braille); // let result = typeface_to_word_mode(&raw_braille); // let result = capitals_to_word_mode(&result); From 6af4178fde8a0c176dcdf28fd883d1704a5b2c73 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 6 Aug 2026 14:34:47 -0700 Subject: [PATCH 015/120] promote munder/mover/munderover base attrs to element -- prevents error!() message. May want to change the promoted name... --- src/chemistry.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/chemistry.rs b/src/chemistry.rs index b8a6ca4fc..f238985c1 100644 --- a/src/chemistry.rs +++ b/src/chemistry.rs @@ -523,6 +523,15 @@ fn set_marked_chemistry_attr(mathml: Element, chem: &str) { mathml.set_attribute_value(CHEM_EQUATION, "true"); } } + "munder" | "mover" | "munderover" => { + // inherit from the base + let base = as_element(mathml.children()[0]); + if let Some(base_chem) = base.attribute(CHEM_EQUATION_OPERATOR) { + mathml.set_attribute_value(CHEM_EQUATION_OPERATOR, base_chem.value()); + } else if let Some(base_chem) = base.attribute(CHEM_FORMULA_OPERATOR) { + mathml.set_attribute_value(CHEM_FORMULA_OPERATOR, base_chem.value()); + } + } _ => error!("Internal error: {} should not be marked as 'MAYBE_CHEMISTRY'", tag_name), } } else if tag_name == "mrow" { From 1046bd53ee4f97bf79c8ec88525ee5db22f44b2e Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 6 Aug 2026 17:50:53 -0700 Subject: [PATCH 016/120] all primes on chem symbols such as R' -- used to be a reject, now neutral --- src/chemistry.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/chemistry.rs b/src/chemistry.rs index f238985c1..4b991dcef 100644 --- a/src/chemistry.rs +++ b/src/chemistry.rs @@ -1020,6 +1020,10 @@ fn likely_chem_superscript(sup: Element) -> i32 { sup.set_attribute_value("data-number", small_roman_to_number(as_str!(as_text(sup)))); sup.set_attribute_value(MAYBE_CHEMISTRY, "2"); return 2; + } else if sup_name == "mo" && !as_text(sup).is_empty() && as_str!(as_text(sup)).trim_matches(['\'', '′', '″', '‴']).is_empty() { + // primes distinguish generic groups (R, R', R'') -- neutral: don't add or rule out chemistry + sup.set_attribute_value(MAYBE_CHEMISTRY, "0"); + return 0; } else if sup_name == "mrow" { // look for something like '2+' let children = sup.children(); From af250aa4cab9c59540fe453f4f3081e4297c61a6 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 6 Aug 2026 17:54:29 -0700 Subject: [PATCH 017/120] Fix leftover value for G1 indicator -- unlike UEB, French braille numbers don't conflict with letters. Add code to handle `;` special case if there are "blocks" in the expr --- src/braille.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/braille.rs b/src/braille.rs index f3442a325..1038cb319 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -2393,7 +2393,7 @@ static FRENCH_INDICATOR_REPLACEMENTS: phf::Map<&str, &str> = phf_map! { "𝔹" => "⠨", // blackboard "T" => "⠈", // script "I" => "⠸", // italic - "1" => "⠀", // Grade 1 symbol (used for number followed by a letter) + "1" => "", // Grade 1 symbol (used for number followed by a letter, but irrelevant for French, but generated by UEB cleanup) "L" => "", // Letter left in to assist in locating letters "D" => "XXX", // German (Deutsche) -- from prefs "G" => "⠘", // Greek @@ -2407,6 +2407,7 @@ static FRENCH_INDICATOR_REPLACEMENTS: phf::Map<&str, &str> = phf_map! { "c" => "⠆", // Closing group indicator "b" => "⠰", // Optional opening group indicator "e" => "⠆", // Optional closing group indicator + ";" => "⠆", // Need to be able to detect ';' and potentially add dot-6 (see 7° Ponctuations) "P" => "⠠⠄", // Math modifier and code change indicator }; @@ -2414,6 +2415,7 @@ static FRENCH_INDICATOR_REPLACEMENTS: phf::Map<&str, &str> = phf_map! { /// Optional pairs nest like parentheses. Resolve outer pairs before inner ones. /// A pair is kept when its content ends with a letter/number (`L`/`N` + braille) before `e`, and a /// letter/number follows the next `b` (or directly) after `e`. +/// Because it fits in the algorithm, dealing with ';' (need to add dot-6 if inside of a block) is also handled fn remove_optional_group_indicators(braille: &str) -> String { let chars: Vec = braille.chars().collect(); let mut stack: Vec = Vec::new(); @@ -2444,11 +2446,23 @@ fn remove_optional_group_indicators(braille: &str) -> String { } } + // 7° Ponctuations: when the formula contains any block symbols (start/end of block, dots 5-6 / + // dots 2-3), a ';' is preceded by dot-6 so it isn't confused with the end-of-block indicator + // (which is also dots 2-3). This applies to every ';' in the formula, not just those nested + // inside a block. Blocks in the output are the surviving 'o'/'c' (literal or converted b/e). + let has_block = convert_to_oc.iter().any(|&converted| converted) + || chars.iter().zip(remove.iter()).any(|(&ch, &removed)| !removed && (ch == 'o' || ch == 'c')); + let mut result = String::with_capacity(braille.len()); for (i, &ch) in chars.iter().enumerate() { if remove[i] { continue; } + // A ';' gets a leading dot-6; the dots-23 itself comes from the ';' -> "⠆" mapping in + // FRENCH_INDICATOR_REPLACEMENTS. + if ch == ';' && has_block { + result.push('⠠'); + } if convert_to_oc[i] { result.push(if ch == 'b' { 'o' } else { 'c' }); } else { @@ -2544,7 +2558,7 @@ fn letter_or_number_after_b( fn french_cleanup(_pref_manager: Ref, raw_braille: String) -> String { // FIX: need to implement this -- this is just a copy of the Vietnam code - static REPLACE_INDICATORS: LazyLock = LazyLock::new(|| Regex::new(r"([1SB𝔹TILDGHC𝐶NWscocbe])").unwrap()); + static REPLACE_INDICATORS: LazyLock = LazyLock::new(|| Regex::new(r"([1SB𝔹TILDGHC𝐶NWscocbe;])").unwrap()); debug!("french_cleanup: start={}", raw_braille); // let result = typeface_to_word_mode(&raw_braille); // let result = capitals_to_word_mode(&result); From f481cd5873cc2511538255dd74a05de0bb8e2fcd Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 6 Aug 2026 17:56:49 -0700 Subject: [PATCH 018/120] Lots of fixes and addition of tests. Now at 185 passed; 4 failed --- Rules/Braille/French/French_Rules.yaml | 139 ++- Rules/Braille/French/definitions.yaml | 4 +- Rules/Braille/French/unicode-full.yaml | 1136 ++++-------------------- Rules/Braille/French/unicode.yaml | 40 +- tests/braille.rs | 1 + tests/braille/French/chemistry.rs | 302 ++++++- tests/braille/French/french.rs | 265 +++++- tests/braille/French/other.rs | 367 ++++++++ 8 files changed, 1239 insertions(+), 1015 deletions(-) create mode 100644 tests/braille/French/other.rs diff --git a/Rules/Braille/French/French_Rules.yaml b/Rules/Braille/French/French_Rules.yaml index 5d224216f..66a14b325 100644 --- a/Rules/Braille/French/French_Rules.yaml +++ b/Rules/Braille/French/French_Rules.yaml @@ -244,8 +244,9 @@ # we include invisible times in the match because some AbbreviationsForGrouping are not function names match: "count(*)=3 and *[2][self::m:mo][.='\u2061'or .='\u2062'] and not(*[3][self::m:mrow][IsBracketed(., '(', ')') or IsBracketed(., '[', ']')]) and - BaseNode(*[1])[self::m:mi][not($French_UseShortForm and IsInDefinition(., 'Braille', 'ShortForms')) and - (IsInDefinition(., 'Braille', 'TrigFunctionNames') or IsInDefinition(., 'Braille', 'AbbreviationsForGrouping'))]" + BaseNode(*[1])[self::m:mi] + [not($French_UseShortForm and IsInDefinition(., 'Braille', 'ShortForms')) and + (IsInDefinition(., 'Braille', 'TrigFunctionNames') or IsInDefinition(., 'Braille', 'AbbreviationsForGrouping'))]" replace: - x: "*[1]" - t: "o" @@ -301,15 +302,13 @@ match: "." replace: - test: - if: "$French_UseShortForm and IsInDefinition(., 'Braille', 'ShortForms')" - then: - - with: - variables: [Replacement: "DefinitionValue(., 'Braille', 'ShortForms')"] - replace: - - test: - if: "translate(substring($Replacement, 1, 1), 'abcdefghijklmnopqrstuvwxyz', '')=''" - then: [x: "BrailleChars($Replacement, 'French')"] - else: [x: "$Replacement"] + if: "$French_UseShortForm" + then_test: + - if: "IsInDefinition(., 'Braille', 'ShortForms')" + then: [x: "DEBUG(DefinitionValue(., 'Braille', 'ShortForms'))"] # sin + - else_if: "IsInDefinition(., 'Braille', 'ShortFormAbbreviations')" # cosh -> ch + then: [x: "BrailleChars(DefinitionValue(., 'Braille', 'ShortFormAbbreviations'), 'French')"] + else: [x: "BrailleChars(., 'French')"] else: [x: "BrailleChars(., 'French')"] - @@ -353,24 +352,72 @@ - x: "*[1]" - - # In French braille, we drop the script indicators in both subscripts and superscripts for: - # msub: chemistry (e.g., H20) - # msup: primes - # We combine them here + # Drop subscript indicator for chemistry (e.g., H₂O → ⠨⠓⠣⠨⠕) + name: single-char-exceptions + tag: msub + match: "@data-chem-formula" + replace: + - x: "*[1]" + - x: "*[2]" + +- + # Drop superscript indicator for primes and degree (units: 37° → ⠩⠻⠐⠕, not …⠈⠐⠕) name: single-char-exceptions - tag: [msub, msup] - match: "(@data-chem-formula) or (self::m:msup and *[2][translate(., \"'′″‴\",'')=''])" + tag: msup + match: "*[2][translate(., \"'′″‴°º\",'')='']" replace: - x: "*[1]" + - x: "*[2]" + +- + # Chemistry ion charges keep ⠈; group when followed by the same operator (chem spec p.9) + # or when the charge itself needs grouping (e.g. 2-) + name: chemistry-charge + tag: msup + match: "@data-chem-formula" + replace: + - x: "*[1]" + - t: "⠈" - test: - # we need to group an operator if it is followed by an operator (see p9 of chem spec) -- don't want 2 "+"s in a row - # it is a bit tricky, we used following and preceding because it might be at a different level of the tree - if: "@data-chem-formula and *[2][self::m:mo] and following::*[1][self::m:mo and .=preceding::*[1]]" + if: "*[2][self::m:mo] and following::*[1][self::m:mo and .=preceding::*[1]]" then: - t: "o" - x: "*[2]" - t: "c" - else: [x: "*[2]"] + else: + - test: + if: "NeedsToBeGrouped(*[2], 'French', false())" + then: + - t: "o" + - x: "*[2]" + - t: "c" + else: [x: "*[2]"] + +- + # Chemistry ion with both an atom count (subscript) and a charge (superscript), e.g. SO₄²⁻. + # The subscript drops its indicator like the msub single-char-exceptions rule (@data-chem-formula), + # and the superscript charge is handled exactly like the chemistry-charge msup rule. + name: chemistry-charge + tag: msubsup + match: "@data-chem-formula" + replace: + - x: "*[1]" + - x: "*[2]" # subscript count: single-char-exception -> no ⠢ indicator + - t: "⠈" + - test: + if: "*[3][self::m:mo] and following::*[1][self::m:mo and .=preceding::*[1]]" + then: + - t: "o" + - x: "*[3]" + - t: "c" + else: + - test: + if: "NeedsToBeGrouped(*[3], 'French', false())" + then: + - t: "o" + - x: "*[3]" + - t: "c" + else: [x: "*[3]"] - name: single-char-exceptions @@ -412,6 +459,56 @@ +- + # Lewis doublets: bars above or below an atom → output "1d" + name: lewis-doublets + tag: [munder, mover] + match: "*[2][.='¯'] and (*[1][@data-chem-element] or ancestor-or-self::*[contains(@intent, ':chemical-formula')] or ancestor-or-self::*[@data-chem-formula])" + replace: + - x: "*[1]" + - t: "⠰⠡⠙⠆" + +- + # Lewis doublets: bars above and below an atom → output "2d" (chem spec §I.5) + name: lewis-doublets + tag: munderover + match: "*[2][.='¯'] and *[3][.='¯'] and (*[1][@data-chem-element] or ancestor-or-self::*[contains(@intent, ':chemical-formula')] or ancestor-or-self::*[@data-chem-formula])" + replace: + - x: "*[1]" + - t: "⠰⠣⠙⠆" + +- + # Chemical equilibrium annotations: ⠸⠻(LTR;RTL) with empty zone = ⠐⠂ (chem spec §III.5) + # Note: the "over" comes first + name: chemical-equilibrium-zones + tag: [munderover, munder, mover] + match: "*[1][.='⇌' or .='⇄']" + replace: + - x: "*[1]" + - t: "⠦" + - test: + - if: "self::m:munder" + then: + - t: "⠐⠂⠆" + - x: "*[2]" + - else_if: "self::m:mover" + then: + - x: "*[2]" + - t: "⠆⠐⠂" + - else_if: "*[2][self::m:mtext and .=' ']" + then: + - x: "*[3]" + - t: "⠆⠐⠂" + - else_if: "*[3][self::m:mtext and .=' ']" + then: + - t: "⠐⠂⠆" + - x: "*[2]" + else: + - x: "*[3]" + - t: "⠆" + - x: "*[2]" + - t: "⠴" + - name: msubsup_default_mmultiscripts_equiv tag: [msubsup, munderover, mmultiscripts] # mmultiscripts with only sub/sup postscript diff --git a/Rules/Braille/French/definitions.yaml b/Rules/Braille/French/definitions.yaml index 8a611c06a..8376ab0d7 100644 --- a/Rules/Braille/French/definitions.yaml +++ b/Rules/Braille/French/definitions.yaml @@ -21,7 +21,9 @@ # ⠘ -> \ue003 "sin": "\ue000", "cos": "\ue001\ue000", "tan": "\ue002", "cotan": "\ue001\ue002", "arcsin": "\ue003\ue000", "arccos": "\ue003\ue001\ue002", "arctan": "\ue003\ue002", "arccotan": "\ue003\ue001\ue002", - + } + +- ShortFormAbbreviations: { "cosh": "ch", "sinh": "sh", "tanh": "th", "coth": "coth", "arsinh": "argsh", "arcosh": "argch", "artanh": "argth", "arcoth": "argcoth", } \ No newline at end of file diff --git a/Rules/Braille/French/unicode-full.yaml b/Rules/Braille/French/unicode-full.yaml index 2f6ecc9b0..e27f214a6 100644 --- a/Rules/Braille/French/unicode-full.yaml +++ b/Rules/Braille/French/unicode-full.yaml @@ -1,4 +1,23 @@ --- + + - " ": [t: "W"] # 0x2000 (en quad, 1/2 em) + - " ": [t: "W"] # 0x2001 (em quad, 1 em) + - " ": [t: "W"] # 0x2002 (en space, 1/2 em) + - " ": [t: "W"] # 0x2003 (em space, 1 em) + - " ": [t: "W"] # 0x2004 (three-per-em, 1/3 em) + - " ": [t: "W"] # 0x2005 (four-per-em, 1/4 em) + - " ": [t: ""] # 0x2006 (six-per-em, 1/6 em) + - " ": [t: "W"] # 0x2007 (figure space) + - " ": [t: "W"] # 0x2008 (punctuation space) + - " ": [t: ""] # 0x2009 (thin space, ~1/5 em) + - " ": [t: ""] # 0x200a (hair space) + - "​": [t: ""] # 0x200b (zero-width space) + - "
": [t: "W"] # 0x2028 (line separator) + - "
": [t: "W"] # 0x2029 (paragraph separator) + - " ": [t: ""] # 0x202f (narrow no-break space, ~thin) + - " ": [t: "W"] # 0x205f (medium math space, 4/18 em) + - "": [t: ""] # 0xfeff (BOM / zero-width no-break space) + - "⇌": [t: "⠸⠻"] # 0x21CC (Rightwards harpoon over leftwards harpoon (equilibrium)) # - "⥂": [t: ""] # 0x2942 (Rightwards arrow above short leftwards arrow (equilibrium, trend to the right)) @@ -33,224 +52,38 @@ - "↉": [t: "⠼⠌⠩"] # 0x2189 (vulgar fraction zero thirds) - - - "𝐚": [t: "BL⠁"] # 0x1d41a - - "𝐛": [t: "BL⠃"] # 0x1d41b - - "𝐜": [t: "BL⠉"] # 0x1d41c - - "𝐝": [t: "BL⠙"] # 0x1d41d - - "𝐞": [t: "BL⠑"] # 0x1d41e - - "𝐟": [t: "BL⠋"] # 0x1d41f - - "𝐠": [t: "BL⠛"] # 0x1d420 - - "𝐡": [t: "BL⠓"] # 0x1d421 - - "𝐢": [t: "BL⠊"] # 0x1d422 - - "𝐣": [t: "BL⠚"] # 0x1d423 - - "𝐤": [t: "BL⠅"] # 0x1d424 - - "𝐥": [t: "BL⠇"] # 0x1d425 - - "𝐦": [t: "BL⠍"] # 0x1d426 - - "𝐧": [t: "BL⠝"] # 0x1d427 - - "𝐨": [t: "BL⠕"] # 0x1d428 - - "𝐩": [t: "BL⠏"] # 0x1d429 - - "𝐪": [t: "BL⠟"] # 0x1d42a - - "𝐫": [t: "BL⠗"] # 0x1d42b - - "𝐬": [t: "BL⠎"] # 0x1d42c - - "𝐭": [t: "BL⠞"] # 0x1d42d - - "𝐮": [t: "BL⠥"] # 0x1d42e - - "𝐯": [t: "BL⠧"] # 0x1d42f - - "𝐰": [t: "BL⠺"] # 0x1d430 - - "𝐱": [t: "BL⠭"] # 0x1d431 - - "𝐲": [t: "BL⠽"] # 0x1d432 - - "𝐳": [t: "BL⠵"] # 0x1d433 - - - "𝐀": [t: "BCL⠁"] # 0x1d400 - - "𝐁": [t: "BCL⠃"] # 0x1d401 - - "𝐂": [t: "BCL⠉"] # 0x1d402 - - "𝐃": [t: "BCL⠙"] # 0x1d403 - - "𝐄": [t: "BCL⠑"] # 0x1d404 - - "𝐅": [t: "BCL⠋"] # 0x1d405 - - "𝐆": [t: "BCL⠛"] # 0x1d406 - - "𝐇": [t: "BCL⠓"] # 0x1d407 - - "𝐈": [t: "BCL⠊"] # 0x1d408 - - "𝐉": [t: "BCL⠚"] # 0x1d409 - - "𝐊": [t: "BCL⠅"] # 0x1d40a - - "𝐋": [t: "BCL⠇"] # 0x1d40b - - "𝐌": [t: "BCL⠍"] # 0x1d40c - - "𝐍": [t: "BCL⠝"] # 0x1d40d - - "𝐎": [t: "BCL⠕"] # 0x1d40e - - "𝐏": [t: "BCL⠏"] # 0x1d40f - - "𝐐": [t: "BCL⠟"] # 0x1d410 - - "𝐑": [t: "BCL⠗"] # 0x1d411 - - "𝐒": [t: "BCL⠎"] # 0x1d412 - - "𝐓": [t: "BCL⠞"] # 0x1d413 - - "𝐔": [t: "BCL⠥"] # 0x1d414 - - "𝐕": [t: "BCL⠧"] # 0x1d415 - - "𝐖": [t: "BCL⠺"] # 0x1d416 - - "𝐗": [t: "BCL⠭"] # 0x1d417 - - "𝐘": [t: "BCL⠽"] # 0x1d418 - - "𝐙": [t: "BCL⠵"] # 0x1d419 - -# don't mark these as italic - - "𝑎": [t: "L⠁"] # 0x1d44e - - "𝑏": [t: "L⠃"] # 0x1d44f - - "𝑐": [t: "L⠉"] # 0x1d450 - - "𝑑": [t: "L⠙"] # 0x1d451 - - "𝑒": [t: "L⠑"] # 0x1d452 - - "𝑓": [t: "L⠋"] # 0x1d453 - - "𝑔": [t: "L⠛"] # 0x1d454 - - "𝑕": [t: "L⠓"] # 0x1d455 - - "𝑖": [t: "L⠊"] # 0x1d456 - - "𝑗": [t: "L⠚"] # 0x1d457 - - "𝑘": [t: "L⠅"] # 0x1d458 - - "𝑙": [t: "L⠇"] # 0x1d459 - - "𝑚": [t: "L⠍"] # 0x1d45a - - "𝑛": [t: "L⠝"] # 0x1d45b - - "𝑜": [t: "L⠕"] # 0x1d45c - - "𝑝": [t: "L⠏"] # 0x1d45d - - "𝑞": [t: "L⠟"] # 0x1d45e - - "𝑟": [t: "L⠗"] # 0x1d45f - - "𝑠": [t: "L⠎"] # 0x1d460 - - "𝑡": [t: "L⠞"] # 0x1d461 - - "𝑢": [t: "L⠥"] # 0x1d462 - - "𝑣": [t: "L⠧"] # 0x1d463 - - "𝑤": [t: "L⠺"] # 0x1d464 - - "𝑥": [t: "L⠭"] # 0x1d465 - - "𝑦": [t: "L⠽"] # 0x1d466 - - "𝑧": [t: "L⠵"] # 0x1d467 - - - "𝐴": [t: "CL⠁"] # 0x1d434 - - "𝐵": [t: "CL⠃"] # 0x1d435 - - "𝐶": [t: "CL⠉"] # 0x1d436 - - "𝐷": [t: "CL⠙"] # 0x1d437 - - "𝐸": [t: "CL⠑"] # 0x1d438 - - "𝐹": [t: "CL⠋"] # 0x1d439 - - "𝐺": [t: "CL⠛"] # 0x1d43a - - "𝐻": [t: "CL⠓"] # 0x1d43b - - "𝐼": [t: "CL⠊"] # 0x1d43c - - "𝐽": [t: "CL⠚"] # 0x1d43d - - "𝐾": [t: "CL⠅"] # 0x1d43e - - "𝐿": [t: "CL⠇"] # 0x1d43f - - "𝑀": [t: "CL⠍"] # 0x1d440 - - "𝑁": [t: "CL⠝"] # 0x1d441 - - "𝑂": [t: "CL⠕"] # 0x1d442 - - "𝑃": [t: "CL⠏"] # 0x1d443 - - "𝑄": [t: "CL⠟"] # 0x1d444 - - "𝑅": [t: "CL⠗"] # 0x1d445 - - "𝑆": [t: "CL⠎"] # 0x1d446 - - "𝑇": [t: "CL⠞"] # 0x1d447 - - "𝑈": [t: "CL⠥"] # 0x1d448 - - "𝑉": [t: "CL⠧"] # 0x1d449 - - "𝑊": [t: "CL⠺"] # 0x1d44a - - "𝑋": [t: "CL⠭"] # 0x1d44b - - "𝑌": [t: "CL⠽"] # 0x1d44c - - "𝑍": [t: "CL⠵"] # 0x1d44d - - - "𝒂": [t: "BIL⠁"] # 0x1d482 - - "𝒃": [t: "BIL⠃"] # 0x1d483 - - "𝒄": [t: "BIL⠉"] # 0x1d484 - - "𝒅": [t: "BIL⠙"] # 0x1d485 - - "𝒆": [t: "BIL⠑"] # 0x1d486 - - "𝒇": [t: "BIL⠋"] # 0x1d487 - - "𝒈": [t: "BIL⠛"] # 0x1d488 - - "𝒉": [t: "BIL⠓"] # 0x1d489 - - "𝒊": [t: "BIL⠊"] # 0x1d48a - - "𝒋": [t: "BIL⠚"] # 0x1d48b - - "𝒌": [t: "BIL⠅"] # 0x1d48c - - "𝒍": [t: "BIL⠇"] # 0x1d48d - - "𝒎": [t: "BIL⠍"] # 0x1d48e - - "𝒏": [t: "BIL⠝"] # 0x1d48f - - "𝒐": [t: "BIL⠕"] # 0x1d490 - - "𝒑": [t: "BIL⠏"] # 0x1d491 - - "𝒒": [t: "BIL⠟"] # 0x1d492 - - "𝒓": [t: "BIL⠗"] # 0x1d493 - - "𝒔": [t: "BIL⠎"] # 0x1d494 - - "𝒕": [t: "BIL⠞"] # 0x1d495 - - "𝒖": [t: "BIL⠥"] # 0x1d496 - - "𝒗": [t: "BIL⠧"] # 0x1d497 - - "𝒘": [t: "BIL⠺"] # 0x1d498 - - "𝒙": [t: "BIL⠭"] # 0x1d499 - - "𝒚": [t: "BIL⠽"] # 0x1d49a - - "𝒛": [t: "BIL⠵"] # 0x1d49b - - - "𝑨": [t: "BICL⠁"] # 0x1d468 - - "𝑩": [t: "BICL⠃"] # 0x1d469 - - "𝑪": [t: "BICL⠉"] # 0x1d46a - - "𝑫": [t: "BICL⠙"] # 0x1d46b - - "𝑬": [t: "BICL⠑"] # 0x1d46c - - "𝑭": [t: "BICL⠋"] # 0x1d46d - - "𝑮": [t: "BICL⠛"] # 0x1d46e - - "𝑯": [t: "BICL⠓"] # 0x1d46f - - "𝑰": [t: "BICL⠊"] # 0x1d470 - - "𝑱": [t: "BICL⠚"] # 0x1d471 - - "𝑲": [t: "BICL⠅"] # 0x1d472 - - "𝑳": [t: "BICL⠇"] # 0x1d473 - - "𝑴": [t: "BICL⠍"] # 0x1d474 - - "𝑵": [t: "BICL⠝"] # 0x1d475 - - "𝑶": [t: "BICL⠕"] # 0x1d476 - - "𝑷": [t: "BICL⠏"] # 0x1d477 - - "𝑸": [t: "BICL⠟"] # 0x1d478 - - "𝑹": [t: "BICL⠗"] # 0x1d479 - - "𝑺": [t: "BICL⠎"] # 0x1d47a - - "𝑻": [t: "BICL⠞"] # 0x1d47b - - "𝑼": [t: "BICL⠥"] # 0x1d47c - - "𝑽": [t: "BICL⠧"] # 0x1d47d - - "𝑾": [t: "BICL⠺"] # 0x1d47e - - "𝑿": [t: "BICL⠭"] # 0x1d47f - - "𝒀": [t: "BICL⠽"] # 0x1d480 - - "𝒁": [t: "BICL⠵"] # 0x1d481 - - - "𝒶": [t: "TsL⠁"] # 0x1d4b6 - - "𝒷": [t: "TsL⠃"] # 0x1d4b7 - - "𝒸": [t: "TsL⠉"] # 0x1d4b8 - - "𝒹": [t: "TsL⠙"] # 0x1d4b9 - - "𝒺": [t: "TsL⠑"] # 0x1d4ba - - "𝒻": [t: "TsL⠋"] # 0x1d4bb - - "𝒼": [t: "TsL⠛"] # 0x1d4bc - - "𝒽": [t: "TsL⠓"] # 0x1d4bd - - "𝒾": [t: "TsL⠊"] # 0x1d4be - - "𝒿": [t: "TsL⠚"] # 0x1d4bf - - "𝓀": [t: "TsL⠅"] # 0x1d4c0 - - "𝓁": [t: "TsL⠇"] # 0x1d4c1 - - "𝓂": [t: "TsL⠍"] # 0x1d4c2 - - "𝓃": [t: "TsL⠝"] # 0x1d4c3 - - "𝓄": [t: "TsL⠕"] # 0x1d4c4 - - "𝓅": [t: "TsL⠏"] # 0x1d4c5 - - "𝓆": [t: "TsL⠟"] # 0x1d4c6 - - "𝓇": [t: "TsL⠗"] # 0x1d4c7 - - "𝓈": [t: "TsL⠎"] # 0x1d4c8 - - "𝓉": [t: "TsL⠞"] # 0x1d4c9 - - "𝓊": [t: "TsL⠥"] # 0x1d4ca - - "𝓋": [t: "TsL⠧"] # 0x1d4cb - - "𝓌": [t: "TsL⠺"] # 0x1d4cc - - "𝓍": [t: "TsL⠭"] # 0x1d4cd - - "𝓎": [t: "TsL⠽"] # 0x1d4ce - - "𝓏": [t: "TsL⠵"] # 0x1d4cf - - - "𝒜": [t: "TsCL⠁"] # 0x1d49c - - "𝒝": [t: "TsCL⠃"] # 0x1d49d - - "𝒞": [t: "TsCL⠉"] # 0x1d49e - - "𝒟": [t: "TsCL⠙"] # 0x1d49f - - "𝒠": [t: "TsCL⠑"] # 0x1d4a0 - - "𝒡": [t: "TsCL⠋"] # 0x1d4a1 - - "𝒢": [t: "TsCL⠛"] # 0x1d4a2 - - "𝒣": [t: "TsCL⠓"] # 0x1d4a3 - - "𝒤": [t: "TsCL⠊"] # 0x1d4a4 - - "𝒥": [t: "TsCL⠚"] # 0x1d4a5 - - "𝒦": [t: "TsCL⠅"] # 0x1d4a6 - - "𝒧": [t: "TsCL⠇"] # 0x1d4a7 - - "𝒨": [t: "TsCL⠍"] # 0x1d4a8 - - "𝒩": [t: "TsCL⠝"] # 0x1d4a9 - - "𝒪": [t: "TsCL⠕"] # 0x1d4aa - - "𝒫": [t: "TsCL⠏"] # 0x1d4ab - - "𝒬": [t: "TsCL⠟"] # 0x1d4ac - - "𝒭": [t: "TsCL⠗"] # 0x1d4ad - - "𝒮": [t: "TsCL⠎"] # 0x1d4ae - - "𝒯": [t: "TsCL⠞"] # 0x1d4af - - "𝒰": [t: "TsCL⠥"] # 0x1d4b0 - - "𝒱": [t: "TsCL⠧"] # 0x1d4b1 - - "𝒲": [t: "TsCL⠺"] # 0x1d4b2 - - "𝒳": [t: "TsCL⠭"] # 0x1d4b3 - - "𝒴": [t: "TsCL⠽"] # 0x1d4b4 - - "𝒵": [t: "TsCL⠵"] # 0x1d4b5 - + # Hebrew letters (H → ⠘⠘); spell via Latin/assoc letters in unicode.yaml + - "אבגדהוזחטיכלמנסעפצקרשת": # 0x05D0–0x05EA (excl. final forms) + - tc: "H" + - spell: "translate('.', 'אבגדהוזחטיכלמנסעפצקרשת', 'abgdhwzxtjklmnsëpèqrît')" + + + + # Mathematical alphanumeric symbols (Latin) — typeface via tc:; C/L from unicode.yaml + - "𝐀-𝐙": # 0x1d400 - 0x1d419 + - tc: "B" + - spell: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝐚-𝐳": # 0x1d41a - 0x1d433 + - tc: "B" + - spell: "translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz')" + - "𝐴-𝑍": # 0x1d434 - 0x1d44d + - tc: "" + - spell: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝑎-𝑧": # 0x1d44e - 0x1d467 + - tc: "" + - spell: "translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔𝑕𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz')" + - "𝑨-𝒁": # 0x1d468 - 0x1d481 + - tc: "BI" + - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝒂-𝒛": # 0x1d482 - 0x1d49b + - tc: "BI" + - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" + - "𝒜-𝒵": # 0x1d49c - 0x1d4b5 + - tc: "Ts" + - spell: "translate('.', '𝒜𝒝𝒞𝒟𝒠𝒡𝒢𝒣𝒤𝒥𝒦𝒧𝒨𝒩𝒪𝒫𝒬𝒭𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝒶-𝓏": # 0x1d4b6 - 0x1d4cf + - tc: "Ts" + - spell: "translate('.', '𝒶𝒷𝒸𝒹𝒺𝒻𝒼𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝓄𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏', 'abcdefghijklmnopqrstuvwxyz')" - "ℊ": [t: "TL⠛⠁"] # 0x210a (Script Small G) - "ℋ": [t: "TCL⠓⠁"] # 0x210b (Script Capital H) - "ℒ": [t: "TCL⠇"] # 0x2113 (Script Capital L) @@ -262,766 +95,137 @@ - "ℱ": [t: "TCL⠋"] # 0x2131 (Script Capital F) - "ℳ": [t: "TCL⠍"] # 0x2133 (Script Capital M) - "ℴ": [t: "TL⠕"] # 0x21334 (Script Small O) - - "𝓪": [t: "BTsL⠁"] # 0x1d4ea - - "𝓫": [t: "BTsL⠃"] # 0x1d4eb - - "𝓬": [t: "BTsL⠉"] # 0x1d4ec - - "𝓭": [t: "BTsL⠙"] # 0x1d4ed - - "𝓮": [t: "BTsL⠑"] # 0x1d4ee - - "𝓯": [t: "BTsL⠋"] # 0x1d4ef - - "𝓰": [t: "BTsL⠛"] # 0x1d4f0 - - "𝓱": [t: "BTsL⠓"] # 0x1d4f1 - - "𝓲": [t: "BTsL⠊"] # 0x1d4f2 - - "𝓳": [t: "BTsL⠚"] # 0x1d4f3 - - "𝓴": [t: "BTsL⠅"] # 0x1d4f4 - - "𝓵": [t: "BTsL⠇"] # 0x1d4f5 - - "𝓶": [t: "BTsL⠍"] # 0x1d4f6 - - "𝓷": [t: "BTsL⠝"] # 0x1d4f7 - - "𝓸": [t: "BTsL⠕"] # 0x1d4f8 - - "𝓹": [t: "BTsL⠏"] # 0x1d4f9 - - "𝓺": [t: "BTsL⠟"] # 0x1d4fa - - "𝓻": [t: "BTsL⠗"] # 0x1d4fb - - "𝓼": [t: "BTsL⠎"] # 0x1d4fc - - "𝓽": [t: "BTsL⠞"] # 0x1d4fd - - "𝓾": [t: "BTsL⠥"] # 0x1d4fe - - "𝓿": [t: "BTsL⠧"] # 0x1d4ff - - "𝔀": [t: "BTsL⠺"] # 0x1d500 - - "𝔁": [t: "BTsL⠭"] # 0x1d501 - - "𝔂": [t: "BTsL⠽"] # 0x1d502 - - "𝔃": [t: "BTsL⠵"] # 0x1d503 - - - "𝓐": [t: "BTsCL⠁"] # 0x1d4d0 - - "𝓑": [t: "BTsCL⠃"] # 0x1d4d1 - - "𝓒": [t: "BTsCL⠉"] # 0x1d4d2 - - "𝓓": [t: "BTsCL⠙"] # 0x1d4d3 - - "𝓔": [t: "BTsCL⠑"] # 0x1d4d4 - - "𝓕": [t: "BTsCL⠋"] # 0x1d4d5 - - "𝓖": [t: "BTsCL⠛"] # 0x1d4d6 - - "𝓗": [t: "BTsCL⠓"] # 0x1d4d7 - - "𝓘": [t: "BTsCL⠊"] # 0x1d4d8 - - "𝓙": [t: "BTsCL⠚"] # 0x1d4d9 - - "𝓚": [t: "BTsCL⠅"] # 0x1d4da - - "𝓛": [t: "BTsCL⠇"] # 0x1d4db - - "𝓜": [t: "BTsCL⠍"] # 0x1d4dc - - "𝓝": [t: "BTsCL⠝"] # 0x1d4dd - - "𝓞": [t: "BTsCL⠕"] # 0x1d4de - - "𝓟": [t: "BTsCL⠏"] # 0x1d4df - - "𝓠": [t: "BTsCL⠟"] # 0x1d4e0 - - "𝓡": [t: "BTsCL⠗"] # 0x1d4e1 - - "𝓢": [t: "BTsCL⠎"] # 0x1d4e2 - - "𝓣": [t: "BTsCL⠞"] # 0x1d4e3 - - "𝓤": [t: "BTsCL⠥"] # 0x1d4e4 - - "𝓥": [t: "BTsCL⠧"] # 0x1d4e5 - - "𝓦": [t: "BTsCL⠺"] # 0x1d4e6 - - "𝓧": [t: "BTsCL⠭"] # 0x1d4e7 - - "𝓨": [t: "BTsCL⠽"] # 0x1d4e8 - - "𝓩": [t: "BTsCL⠵"] # 0x1d4e9 - - - "𝔞": [t: "DL⠁"] # 0x1d51e - - "𝔟": [t: "DL⠃"] # 0x1d51f - - "𝔠": [t: "DL⠉"] # 0x1d520 - - "𝔡": [t: "DL⠙"] # 0x1d521 - - "𝔢": [t: "DL⠑"] # 0x1d522 - - "𝔣": [t: "DL⠋"] # 0x1d523 - - "𝔤": [t: "DL⠛"] # 0x1d524 - - "𝔥": [t: "DL⠓"] # 0x1d525 - - "𝔦": [t: "DL⠊"] # 0x1d526 - - "𝔧": [t: "DL⠚"] # 0x1d527 - - "𝔨": [t: "DL⠅"] # 0x1d528 - - "𝔩": [t: "DL⠇"] # 0x1d529 - - "𝔪": [t: "DL⠍"] # 0x1d52a - - "𝔫": [t: "DL⠝"] # 0x1d52b - - "𝔬": [t: "DL⠕"] # 0x1d52c - - "𝔭": [t: "DL⠏"] # 0x1d52d - - "𝔮": [t: "DL⠟"] # 0x1d52e - - "𝔯": [t: "DL⠗"] # 0x1d52f - - "𝔰": [t: "DL⠎"] # 0x1d530 - - "𝔱": [t: "DL⠞"] # 0x1d531 - - "𝔲": [t: "DL⠥"] # 0x1d532 - - "𝔳": [t: "DL⠧"] # 0x1d533 - - "𝔴": [t: "DL⠺"] # 0x1d534 - - "𝔵": [t: "DL⠭"] # 0x1d535 - - "𝔶": [t: "DL⠽"] # 0x1d536 - - "𝔷": [t: "DL⠵"] # 0x1d537 - - - "𝔄": [t: "DCL⠁"] # 0x1d504 - - "𝔅": [t: "DCL⠃"] # 0x1d505 - - "𝔆": [t: "DCL⠉"] # 0x1d506 - - "𝔇": [t: "DCL⠙"] # 0x1d507 - - "𝔈": [t: "DCL⠑"] # 0x1d508 - - "𝔉": [t: "DCL⠋"] # 0x1d509 - - "𝔊": [t: "DCL⠛"] # 0x1d50a - - "𝔋": [t: "DCL⠓"] # 0x1d50b - - "𝔌": [t: "DCL⠊"] # 0x1d50c - - "𝔍": [t: "DCL⠚"] # 0x1d50d - - "𝔎": [t: "DCL⠅"] # 0x1d50e - - "𝔏": [t: "DCL⠇"] # 0x1d50f - - "𝔐": [t: "DCL⠍"] # 0x1d510 - - "𝔑": [t: "DCL⠝"] # 0x1d511 - - "𝔒": [t: "DCL⠕"] # 0x1d512 - - "𝔓": [t: "DCL⠏"] # 0x1d513 - - "𝔔": [t: "DCL⠟"] # 0x1d514 - - "𝔕": [t: "DCL⠗"] # 0x1d515 - - "𝔖": [t: "DCL⠎"] # 0x1d516 - - "𝔗": [t: "DCL⠞"] # 0x1d517 - - "𝔘": [t: "DCL⠥"] # 0x1d518 - - "𝔙": [t: "DCL⠧"] # 0x1d519 - - "𝔚": [t: "DCL⠺"] # 0x1d51a - - "𝔛": [t: "DCL⠭"] # 0x1d51b - - "𝔜": [t: "DCL⠽"] # 0x1d51c - - "𝔝": [t: "DCL⠵"] # 0x1d51d + - "𝓐-𝓩": # 0x1d4d0 - 0x1d4e9 + - tc: "BTs" + - spell: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝓪-𝔃": # 0x1d4ea - 0x1d503 + - tc: "BTs" + - spell: "translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz')" + - "𝔄-𝔝": # 0x1d504 - 0x1d51d + - tc: "D" + - spell: "translate('.', '𝔄𝔅𝔆𝔇𝔈𝔉𝔊𝔋𝔌𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔕𝔖𝔗𝔘𝔙𝔚𝔛𝔜𝔝', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝔞-𝔷": # 0x1d51e - 0x1d537 + - tc: "D" + - spell: "translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz')" - "ℌ": [t: "DCL⠓"] # 0x210C (Fraktur Capital H) - "ℑ": [t: "DCL⠊"] # 0x2111 (Fraktur Capital I) - "ℜ": [t: "DCL⠗"] # 0x211C (Fraktur Capital R) - "ℨ": [t: "DCL⠵"] # 0x2128 (Fraktur Capital Z) - "ℭ": [t: "DCL⠉"] # 0x22DC (Fraktur Capital C) - - "𝕒": [t: "𝔹L⠁"] # 0x1d552 - - "𝕓": [t: "𝔹L⠃"] # 0x1d553 - - "𝕔": [t: "𝔹L⠉"] # 0x1d554 - - "𝕕": [t: "𝔹L⠙"] # 0x1d555 - - "𝕖": [t: "𝔹L⠑"] # 0x1d556 - - "𝕗": [t: "𝔹L⠋"] # 0x1d557 - - "𝕘": [t: "𝔹L⠛"] # 0x1d558 - - "𝕙": [t: "𝔹L⠓"] # 0x1d559 - - "𝕚": [t: "𝔹L⠊"] # 0x1d55a - - "𝕛": [t: "𝔹L⠚"] # 0x1d55b - - "𝕜": [t: "𝔹L⠅"] # 0x1d55c - - "𝕝": [t: "𝔹L⠇"] # 0x1d55d - - "𝕞": [t: "𝔹L⠍"] # 0x1d55e - - "𝕟": [t: "𝔹L⠝"] # 0x1d55f - - "𝕠": [t: "𝔹L⠕"] # 0x1d560 - - "𝕡": [t: "𝔹L⠏"] # 0x1d561 - - "𝕢": [t: "𝔹L⠟"] # 0x1d562 - - "𝕣": [t: "𝔹L⠗"] # 0x1d563 - - "𝕤": [t: "𝔹L⠎"] # 0x1d564 - - "𝕥": [t: "𝔹L⠞"] # 0x1d565 - - "𝕦": [t: "𝔹L⠥"] # 0x1d566 - - "𝕧": [t: "𝔹L⠧"] # 0x1d567 - - "𝕨": [t: "𝔹L⠺"] # 0x1d568 - - "𝕩": [t: "𝔹L⠭"] # 0x1d569 - - "𝕪": [t: "𝔹L⠽"] # 0x1d56a - - "𝕫": [t: "𝔹L⠵"] # 0x1d56b - - "𝔸": [t: "𝔹CL⠁"] # 0x1d538 - - "𝔹": [t: "𝔹CL⠃"] # 0x1d539 + - "𝔸-𝔹": # 0x1d538 - 0x1d539 + - tc: "𝔹" + - spell: "translate('.', '𝔸𝔹', 'AB')" + - "𝔻-𝔾": # 0x1d53b - 0x1d53e + - tc: "𝔹" + - spell: "translate('.', '𝔻𝔼𝔽𝔾', 'DEFG')" + - "𝕀-𝕄": # 0x1d540 - 0x1d544 + - tc: "𝔹" + - spell: "translate('.', '𝕀𝕁𝕂𝕃𝕄', 'IJKLM')" + - "𝕆": [t: "𝔹CL⠕"] # 0x1d546 - "ℂ": [t: "𝔹CL⠉"] # 0x2102 - - "𝔻": [t: "𝔹CL⠙"] # 0x1d53b - - "𝔼": [t: "𝔹CL⠑"] # 0x1d53c - - "𝔽": [t: "𝔹CL⠋"] # 0x1d53d - - "𝔾": [t: "𝔹CL⠛"] # 0x1d53e - "ℍ": [t: "𝔹CL⠓"] # 0x210d - - "𝕀": [t: "𝔹CL⠊"] # 0x1d540 - - "𝕁": [t: "𝔹CL⠚"] # 0x1d541 - - "𝕂": [t: "𝔹CL⠅"] # 0x1d542 - - "𝕃": [t: "𝔹CL⠇"] # 0x1d543 - - "𝕄": [t: "𝔹CL⠍"] # 0x1d544 - "ℕ": [t: "𝔹CL⠝"] # 0x2115 - - "𝕆": [t: "𝔹CL⠕"] # 0x1d546 - "ℙ": [t: "𝔹CL⠏"] # 0x2119 - "ℚ": [t: "𝔹CL⠟"] # 0x211a - "ℝ": [t: "𝔹CL⠗"] # 0x211d - - "𝕊": [t: "𝔹CL⠎"] # 0x1d54a - - "𝕋": [t: "𝔹CL⠞"] # 0x1d54b - - "𝕌": [t: "𝔹CL⠥"] # 0x1d54c - - "𝕍": [t: "𝔹CL⠧"] # 0x1d54d - - "𝕎": [t: "𝔹CL⠺"] # 0x1d54e - - "𝕏": [t: "𝔹CL⠭"] # 0x1d54f - - "𝕐": [t: "𝔹CL⠽"] # 0x1d550 - "ℤ": [t: "𝔹CL⠵"] # 0x2124 - - "𝖆": [t: "BDL⠁"] # 0x1d586 - - "𝖇": [t: "BDL⠃"] # 0x1d587 - - "𝖈": [t: "BDL⠉"] # 0x1d588 - - "𝖉": [t: "BDL⠙"] # 0x1d589 - - "𝖊": [t: "BDL⠑"] # 0x1d58a - - "𝖋": [t: "BDL⠋"] # 0x1d58b - - "𝖌": [t: "BDL⠛"] # 0x1d58c - - "𝖍": [t: "BDL⠓"] # 0x1d58d - - "𝖎": [t: "BDL⠊"] # 0x1d58e - - "𝖏": [t: "BDL⠚"] # 0x1d58f - - "𝖐": [t: "BDL⠅"] # 0x1d590 - - "𝖑": [t: "BDL⠇"] # 0x1d591 - - "𝖒": [t: "BDL⠍"] # 0x1d592 - - "𝖓": [t: "BDL⠝"] # 0x1d593 - - "𝖔": [t: "BDL⠕"] # 0x1d594 - - "𝖕": [t: "BDL⠏"] # 0x1d595 - - "𝖖": [t: "BDL⠟"] # 0x1d596 - - "𝖗": [t: "BDL⠗"] # 0x1d597 - - "𝖘": [t: "BDL⠎"] # 0x1d598 - - "𝖙": [t: "BDL⠞"] # 0x1d599 - - "𝖚": [t: "BDL⠥"] # 0x1d59a - - "𝖛": [t: "BDL⠧"] # 0x1d59b - - "𝖜": [t: "BDL⠺"] # 0x1d59c - - "𝖝": [t: "BDL⠭"] # 0x1d59d - - "𝖞": [t: "BDL⠽"] # 0x1d59e - - "𝖟": [t: "BDL⠵"] # 0x1d59f - - - "𝕬": [t: "BDCL⠁"] # 0x1d56c - - "𝕭": [t: "BDCL⠃"] # 0x1d56d - - "𝕮": [t: "BDCL⠉"] # 0x1d56e - - "𝕯": [t: "BDCL⠙"] # 0x1d56f - - "𝕰": [t: "BDCL⠑"] # 0x1d570 - - "𝕱": [t: "BDCL⠋"] # 0x1d571 - - "𝕲": [t: "BDCL⠛"] # 0x1d572 - - "𝕳": [t: "BDCL⠓"] # 0x1d573 - - "𝕴": [t: "BDCL⠊"] # 0x1d574 - - "𝕵": [t: "BDCL⠚"] # 0x1d575 - - "𝕶": [t: "BDCL⠅"] # 0x1d576 - - "𝕷": [t: "BDCL⠇"] # 0x1d577 - - "𝕸": [t: "BDCL⠍"] # 0x1d578 - - "𝕹": [t: "BDCL⠝"] # 0x1d579 - - "𝕺": [t: "BDCL⠕"] # 0x1d57a - - "𝕻": [t: "BDCL⠏"] # 0x1d57b - - "𝕼": [t: "BDCL⠟"] # 0x1d57c - - "𝕽": [t: "BDCL⠗"] # 0x1d57d - - "𝕾": [t: "BDCL⠎"] # 0x1d57e - - "𝕿": [t: "BDCL⠞"] # 0x1d57f - - "𝖀": [t: "BDCL⠥"] # 0x1d580 - - "𝖁": [t: "BDCL⠧"] # 0x1d581 - - "𝖂": [t: "BDCL⠺"] # 0x1d582 - - "𝖃": [t: "BDCL⠭"] # 0x1d583 - - "𝖄": [t: "BDCL⠽"] # 0x1d584 - - "𝖅": [t: "BDCL⠵"] # 0x1d585 - - - "𝖺": [t: "SL⠁"] # 0x1d5ba - - "𝖻": [t: "SL⠃"] # 0x1d5bb - - "𝖼": [t: "SL⠉"] # 0x1d5bc - - "𝖽": [t: "SL⠙"] # 0x1d5bd - - "𝖾": [t: "SL⠑"] # 0x1d5be - - "𝖿": [t: "SL⠋"] # 0x1d5bf - - "𝗀": [t: "SL⠛"] # 0x1d5c0 - - "𝗁": [t: "SL⠓"] # 0x1d5c1 - - "𝗂": [t: "SL⠊"] # 0x1d5c2 - - "𝗃": [t: "SL⠚"] # 0x1d5c3 - - "𝗄": [t: "SL⠅"] # 0x1d5c4 - - "𝗅": [t: "SL⠇"] # 0x1d5c5 - - "𝗆": [t: "SL⠍"] # 0x1d5c6 - - "𝗇": [t: "SL⠝"] # 0x1d5c7 - - "𝗈": [t: "SL⠕"] # 0x1d5c8 - - "𝗉": [t: "SL⠏"] # 0x1d5c9 - - "𝗊": [t: "SL⠟"] # 0x1d5ca - - "𝗋": [t: "SL⠗"] # 0x1d5cb - - "𝗌": [t: "SL⠎"] # 0x1d5cc - - "𝗍": [t: "SL⠞"] # 0x1d5cd - - "𝗎": [t: "SL⠥"] # 0x1d5ce - - "𝗏": [t: "SL⠧"] # 0x1d5cf - - "𝗐": [t: "SL⠺"] # 0x1d5d0 - - "𝗑": [t: "SL⠭"] # 0x1d5d1 - - "𝗒": [t: "SL⠽"] # 0x1d5d2 - - "𝗓": [t: "SL⠵"] # 0x1d5d3 - - - "𝖠": [t: "SCL⠁"] # 0x1d5a0 - - "𝖡": [t: "SCL⠃"] # 0x1d5a1 - - "𝖢": [t: "SCL⠉"] # 0x1d5a2 - - "𝖣": [t: "SCL⠙"] # 0x1d5a3 - - "𝖤": [t: "SCL⠑"] # 0x1d5a4 - - "𝖥": [t: "SCL⠋"] # 0x1d5a5 - - "𝖦": [t: "SCL⠛"] # 0x1d5a6 - - "𝖧": [t: "SCL⠓"] # 0x1d5a7 - - "𝖨": [t: "SCL⠊"] # 0x1d5a8 - - "𝖩": [t: "SCL⠚"] # 0x1d5a9 - - "𝖪": [t: "SCL⠅"] # 0x1d5aa - - "𝖫": [t: "SCL⠇"] # 0x1d5ab - - "𝖬": [t: "SCL⠍"] # 0x1d5ac - - "𝖭": [t: "SCL⠝"] # 0x1d5ad - - "𝖮": [t: "SCL⠕"] # 0x1d5ae - - "𝖯": [t: "SCL⠏"] # 0x1d5af - - "𝖰": [t: "SCL⠟"] # 0x1d5b0 - - "𝖱": [t: "SCL⠗"] # 0x1d5b1 - - "𝖲": [t: "SCL⠎"] # 0x1d5b2 - - "𝖳": [t: "SCL⠞"] # 0x1d5b3 - - "𝖴": [t: "SCL⠥"] # 0x1d5b4 - - "𝖵": [t: "SCL⠧"] # 0x1d5b5 - - "𝖶": [t: "SCL⠺"] # 0x1d5b6 - - "𝖷": [t: "SCL⠭"] # 0x1d5b7 - - "𝖸": [t: "SCL⠽"] # 0x1d5b8 - - "𝖹": [t: "SCL⠵"] # 0x1d5b9 - - - "𝗮": [t: "BSL⠁"] # 0x1d5ee - - "𝗯": [t: "BSL⠃"] # 0x1d5ef - - "𝗰": [t: "BSL⠉"] # 0x1d5f0 - - "𝗱": [t: "BSL⠙"] # 0x1d5f1 - - "𝗲": [t: "BSL⠑"] # 0x1d5f2 - - "𝗳": [t: "BSL⠋"] # 0x1d5f3 - - "𝗴": [t: "BSL⠛"] # 0x1d5f4 - - "𝗵": [t: "BSL⠓"] # 0x1d5f5 - - "𝗶": [t: "BSL⠊"] # 0x1d5f6 - - "𝗷": [t: "BSL⠚"] # 0x1d5f7 - - "𝗸": [t: "BSL⠅"] # 0x1d5f8 - - "𝗹": [t: "BSL⠇"] # 0x1d5f9 - - "𝗺": [t: "BSL⠍"] # 0x1d5fa - - "𝗻": [t: "BSL⠝"] # 0x1d5fb - - "𝗼": [t: "BSL⠕"] # 0x1d5fc - - "𝗽": [t: "BSL⠏"] # 0x1d5fd - - "𝗾": [t: "BSL⠟"] # 0x1d5fe - - "𝗿": [t: "BSL⠗"] # 0x1d5ff - - "𝘀": [t: "BSL⠎"] # 0x1d600 - - "𝘁": [t: "BSL⠞"] # 0x1d601 - - "𝘂": [t: "BSL⠥"] # 0x1d602 - - "𝘃": [t: "BSL⠧"] # 0x1d603 - - "𝘄": [t: "BSL⠺"] # 0x1d604 - - "𝘅": [t: "BSL⠭"] # 0x1d605 - - "𝘆": [t: "BSL⠽"] # 0x1d606 - - "𝘇": [t: "BSL⠵"] # 0x1d607 - - - "𝗔": [t: "BSCL⠁"] # 0x1d5d4 - - "𝗕": [t: "BSCL⠃"] # 0x1d5d5 - - "𝗖": [t: "BSCL⠉"] # 0x1d5d6 - - "𝗗": [t: "BSCL⠙"] # 0x1d5d7 - - "𝗘": [t: "BSCL⠑"] # 0x1d5d8 - - "𝗙": [t: "BSCL⠋"] # 0x1d5d9 - - "𝗚": [t: "BSCL⠛"] # 0x1d5da - - "𝗛": [t: "BSCL⠓"] # 0x1d5db - - "𝗜": [t: "BSCL⠊"] # 0x1d5dc - - "𝗝": [t: "BSCL⠚"] # 0x1d5dd - - "𝗞": [t: "BSCL⠅"] # 0x1d5de - - "𝗟": [t: "BSCL⠇"] # 0x1d5df - - "𝗠": [t: "BSCL⠍"] # 0x1d5e0 - - "𝗡": [t: "BSCL⠝"] # 0x1d5e1 - - "𝗢": [t: "BSCL⠕"] # 0x1d5e2 - - "𝗣": [t: "BSCL⠏"] # 0x1d5e3 - - "𝗤": [t: "BSCL⠟"] # 0x1d5e4 - - "𝗥": [t: "BSCL⠗"] # 0x1d5e5 - - "𝗦": [t: "BSCL⠎"] # 0x1d5e6 - - "𝗧": [t: "BSCL⠞"] # 0x1d5e7 - - "𝗨": [t: "BSCL⠥"] # 0x1d5e8 - - "𝗩": [t: "BSCL⠧"] # 0x1d5e9 - - "𝗪": [t: "BSCL⠺"] # 0x1d5ea - - "𝗫": [t: "BSCL⠭"] # 0x1d5eb - - "𝗬": [t: "BSCL⠽"] # 0x1d5ec - - "𝗭": [t: "BSCL⠵"] # 0x1d5ed - - - "𝘢": [t: "ISL⠁"] # 0x1d622 - - "𝘣": [t: "ISL⠃"] # 0x1d623 - - "𝘤": [t: "ISL⠉"] # 0x1d624 - - "𝘥": [t: "ISL⠙"] # 0x1d625 - - "𝘦": [t: "ISL⠑"] # 0x1d626 - - "𝘧": [t: "ISL⠋"] # 0x1d627 - - "𝘨": [t: "ISL⠛"] # 0x1d628 - - "𝘩": [t: "ISL⠓"] # 0x1d629 - - "𝘪": [t: "ISL⠊"] # 0x1d62a - - "𝘫": [t: "ISL⠚"] # 0x1d62b - - "𝘬": [t: "ISL⠅"] # 0x1d62c - - "𝘭": [t: "ISL⠇"] # 0x1d62d - - "𝘮": [t: "ISL⠍"] # 0x1d62e - - "𝘯": [t: "ISL⠝"] # 0x1d62f - - "𝘰": [t: "ISL⠕"] # 0x1d630 - - "𝘱": [t: "ISL⠏"] # 0x1d631 - - "𝘲": [t: "ISL⠟"] # 0x1d632 - - "𝘳": [t: "ISL⠗"] # 0x1d633 - - "𝘴": [t: "ISL⠎"] # 0x1d634 - - "𝘵": [t: "ISL⠞"] # 0x1d635 - - "𝘶": [t: "ISL⠥"] # 0x1d636 - - "𝘷": [t: "ISL⠧"] # 0x1d637 - - "𝘸": [t: "ISL⠺"] # 0x1d638 - - "𝘹": [t: "ISL⠭"] # 0x1d639 - - "𝘺": [t: "ISL⠽"] # 0x1d63a - - "𝘻": [t: "ISL⠵"] # 0x1d63b - - - "𝘈": [t: "ISCL⠁"] # 0x1d608 - - "𝘉": [t: "ISCL⠃"] # 0x1d609 - - "𝘊": [t: "ISCL⠉"] # 0x1d60a - - "𝘋": [t: "ISCL⠙"] # 0x1d60b - - "𝘌": [t: "ISCL⠑"] # 0x1d60c - - "𝘍": [t: "ISCL⠋"] # 0x1d60d - - "𝘎": [t: "ISCL⠛"] # 0x1d60e - - "𝘏": [t: "ISCL⠓"] # 0x1d60f - - "𝘐": [t: "ISCL⠊"] # 0x1d610 - - "𝘑": [t: "ISCL⠚"] # 0x1d611 - - "𝘒": [t: "ISCL⠅"] # 0x1d612 - - "𝘓": [t: "ISCL⠇"] # 0x1d613 - - "𝘔": [t: "ISCL⠍"] # 0x1d614 - - "𝘕": [t: "ISCL⠝"] # 0x1d615 - - "𝘖": [t: "ISCL⠕"] # 0x1d616 - - "𝘗": [t: "ISCL⠏"] # 0x1d617 - - "𝘘": [t: "ISCL⠟"] # 0x1d618 - - "𝘙": [t: "ISCL⠗"] # 0x1d619 - - "𝘚": [t: "ISCL⠎"] # 0x1d61a - - "𝘛": [t: "ISCL⠞"] # 0x1d61b - - "𝘜": [t: "ISCL⠥"] # 0x1d61c - - "𝘝": [t: "ISCL⠧"] # 0x1d61d - - "𝘞": [t: "ISCL⠺"] # 0x1d61e - - "𝘟": [t: "ISCL⠭"] # 0x1d61f - - "𝘠": [t: "ISCL⠽"] # 0x1d620 - - "𝘡": [t: "ISCL⠵"] # 0x1d621 - - - "𝙖": [t: "BILS⠁"] # 0x1d656 - - "𝙗": [t: "BILS⠃"] # 0x1d657 - - "𝙘": [t: "BILS⠉"] # 0x1d658 - - "𝙙": [t: "BILS⠙"] # 0x1d659 - - "𝙚": [t: "BILS⠑"] # 0x1d65a - - "𝙛": [t: "BILS⠋"] # 0x1d65b - - "𝙜": [t: "BILS⠛"] # 0x1d65c - - "𝙝": [t: "BILS⠓"] # 0x1d65d - - "𝙞": [t: "BILS⠊"] # 0x1d65e - - "𝙟": [t: "BILS⠚"] # 0x1d65f - - "𝙠": [t: "BILS⠅"] # 0x1d660 - - "𝙡": [t: "BILS⠇"] # 0x1d661 - - "𝙢": [t: "BILS⠍"] # 0x1d662 - - "𝙣": [t: "BILS⠝"] # 0x1d663 - - "𝙤": [t: "BILS⠕"] # 0x1d664 - - "𝙥": [t: "BILS⠏"] # 0x1d665 - - "𝙦": [t: "BILS⠟"] # 0x1d666 - - "𝙧": [t: "BILS⠗"] # 0x1d667 - - "𝙨": [t: "BILS⠎"] # 0x1d668 - - "𝙩": [t: "BILS⠞"] # 0x1d669 - - "𝙪": [t: "BILS⠥"] # 0x1d66a - - "𝙫": [t: "BILS⠧"] # 0x1d66b - - "𝙬": [t: "BILS⠺"] # 0x1d66c - - "𝙭": [t: "BILS⠭"] # 0x1d66d - - "𝙮": [t: "BILS⠽"] # 0x1d66e - - "𝙯": [t: "BILS⠵"] # 0x1d66f - - - "𝘼": [t: "BILSC⠁"] # 0x1d63c - - "𝘽": [t: "BILSC⠃"] # 0x1d63d - - "𝘾": [t: "BILSC⠉"] # 0x1d63e - - "𝘿": [t: "BILSC⠙"] # 0x1d63f - - "𝙀": [t: "BILSC⠑"] # 0x1d640 - - "𝙁": [t: "BILSC⠋"] # 0x1d641 - - "𝙂": [t: "BILSC⠛"] # 0x1d642 - - "𝙃": [t: "BILSC⠓"] # 0x1d643 - - "𝙄": [t: "BILSC⠊"] # 0x1d644 - - "𝙅": [t: "BILSC⠚"] # 0x1d645 - - "𝙆": [t: "BILSC⠅"] # 0x1d646 - - "𝙇": [t: "BILSC⠇"] # 0x1d647 - - "𝙈": [t: "BILSC⠍"] # 0x1d648 - - "𝙉": [t: "BILSC⠝"] # 0x1d649 - - "𝙊": [t: "BILSC⠕"] # 0x1d64a - - "𝙋": [t: "BILSC⠏"] # 0x1d64b - - "𝙌": [t: "BILSC⠟"] # 0x1d64c - - "𝙍": [t: "BILSC⠗"] # 0x1d64d - - "𝙎": [t: "BILSC⠎"] # 0x1d64e - - "𝙏": [t: "BILSC⠞"] # 0x1d64f - - "𝙐": [t: "BILSC⠥"] # 0x1d650 - - "𝙑": [t: "BILSC⠧"] # 0x1d651 - - "𝙒": [t: "BILSC⠺"] # 0x1d652 - - "𝙓": [t: "BILSC⠭"] # 0x1d653 - - "𝙔": [t: "BILSC⠽"] # 0x1d654 - - "𝙕": [t: "BILSC⠵"] # 0x1d655 - - - "𝚊": [t: "L⠁"] # 0x1d68a - - "𝚋": [t: "L⠃"] # 0x1d68b - - "𝚌": [t: "L⠉"] # 0x1d68c - - "𝚍": [t: "L⠙"] # 0x1d68d - - "𝚎": [t: "L⠑"] # 0x1d68e - - "𝚏": [t: "L⠋"] # 0x1d68f - - "𝚐": [t: "L⠛"] # 0x1d690 - - "𝚑": [t: "L⠓"] # 0x1d691 - - "𝚒": [t: "L⠊"] # 0x1d692 - - "𝚓": [t: "L⠚"] # 0x1d693 - - "𝚔": [t: "L⠅"] # 0x1d694 - - "𝚕": [t: "L⠇"] # 0x1d695 - - "𝚖": [t: "L⠍"] # 0x1d696 - - "𝚗": [t: "L⠝"] # 0x1d697 - - "𝚘": [t: "L⠕"] # 0x1d698 - - "𝚙": [t: "L⠏"] # 0x1d699 - - "𝚚": [t: "L⠟"] # 0x1d69a - - "𝚛": [t: "L⠗"] # 0x1d69b - - "𝚜": [t: "L⠎"] # 0x1d69c - - "𝚝": [t: "L⠞"] # 0x1d69d - - "𝚞": [t: "L⠥"] # 0x1d69e - - "𝚟": [t: "L⠧"] # 0x1d69f - - "𝚠": [t: "L⠺"] # 0x1d6a0 - - "𝚡": [t: "L⠭"] # 0x1d6a1 - - "𝚢": [t: "L⠽"] # 0x1d6a2 - - "𝚣": [t: "L⠵"] # 0x1d6a3 - - - "𝙰": [t: "CL⠁"] # 0x1d670 - - "𝙱": [t: "CL⠃"] # 0x1d671 - - "𝙲": [t: "CL⠉"] # 0x1d672 - - "𝙳": [t: "CL⠙"] # 0x1d673 - - "𝙴": [t: "CL⠑"] # 0x1d674 - - "𝙵": [t: "CL⠋"] # 0x1d675 - - "𝙶": [t: "CL⠛"] # 0x1d676 - - "𝙷": [t: "CL⠓"] # 0x1d677 - - "𝙸": [t: "CL⠊"] # 0x1d678 - - "𝙹": [t: "CL⠚"] # 0x1d679 - - "𝙺": [t: "CL⠅"] # 0x1d67a - - "𝙻": [t: "CL⠇"] # 0x1d67b - - "𝙼": [t: "CL⠍"] # 0x1d67c - - "𝙽": [t: "CL⠝"] # 0x1d67d - - "𝙾": [t: "CL⠕"] # 0x1d67e - - "𝙿": [t: "CL⠏"] # 0x1d67f - - "𝚀": [t: "CL⠟"] # 0x1d680 - - "𝚁": [t: "CL⠗"] # 0x1d681 - - "𝚂": [t: "CL⠎"] # 0x1d682 - - "𝚃": [t: "CL⠞"] # 0x1d683 - - "𝚄": [t: "CL⠥"] # 0x1d684 - - "𝚅": [t: "CL⠧"] # 0x1d685 - - "𝚆": [t: "CL⠺"] # 0x1d686 - - "𝚇": [t: "CL⠭"] # 0x1d687 - - "𝚈": [t: "CL⠽"] # 0x1d688 - - "𝚉": [t: "CL⠵"] # 0x1d689 - - - "𝚤": [t: "L⠊"] # 0x1d6a4 (dotless i) - - "𝚥": [t: "L⠚"] # 0x1d6a5 (dotless j) - - "𝛂": [t: "BGL⠁"] # 0x1d6c2 - - "𝛃": [t: "BGL⠃"] # 0x1d6c3 - - "𝛄": [t: "BGL⠛"] # 0x1d6c4 - - "𝛅": [t: "BGL⠙"] # 0x1d6c5 - - "𝛆": [t: "BGL⠑"] # 0x1d6c6 - - "𝛇": [t: "BGL⠵"] # 0x1d6c7 - - "𝛈": [t: "BGL⠱ "] # 0x1d6c8 - - "𝛉": [t: "BGL⠹"] # 0x1d6c9 - - "𝛊": [t: "BGL⠊"] # 0x1d6ca - - "𝛋": [t: "BGL⠅"] # 0x1d6cb - - "𝛌": [t: "BGL⠇"] # 0x1d6cc - - "𝛍": [t: "BGL⠍"] # 0x1d6cd - - "𝛎": [t: "BGL⠝"] # 0x1d6ce - - "𝛏": [t: "BGL⠭"] # 0x1d6cf - - "𝛐": [t: "BGL⠕"] # 0x1d6d0 - - "𝛑": [t: "BGL⠏"] # 0x1d6d1 - - "𝛒": [t: "BGL⠗"] # 0x1d6d2 - - "𝛓": [t: "BGL⠎"] # 0x1d6d3 - - "𝛔": [t: "BGL⠞"] # 0x1d6d4 - - "𝛕": [t: "BGL⠥"] # 0x1d6d5 - - "𝛖": [t: "BGL⠋"] # 0x1d6d6 - - "𝛗": [t: "BGL⠯"] # 0x1d6d7 - - "𝛘": [t: "BGL⠽"] # 0x1d6d8 - - "𝛙": [t: "BGL⠺"] # 0x1d6d9 - - - "𝚨": [t: "BCGL⠁"] # 0x1d6a8 - - "𝚩": [t: "BCGL⠃"] # 0x1d6a9 - - "𝚪": [t: "BCGL⠛"] # 0x1d6aa - - "𝚫": [t: "BCGL⠙"] # 0x1d6ab - - "𝚬": [t: "BCGL⠑"] # 0x1d6ac - - "𝚭": [t: "BCGL⠵"] # 0x1d6ad - - "𝚮": [t: "BCGL⠱ "] # 0x1d6ae - - "𝚯": [t: "BCGL⠹"] # 0x1d6af - - "𝚰": [t: "BCGL⠊"] # 0x1d6b0 - - "𝚱": [t: "BCGL⠅"] # 0x1d6b1 - - "𝚲": [t: "BCGL⠇"] # 0x1d6b2 - - "𝚳": [t: "BCGL⠍"] # 0x1d6b3 - - "𝚴": [t: "BCGL⠝"] # 0x1d6b4 - - "𝚵": [t: "BCGL⠭"] # 0x1d6b5 - - "𝚶": [t: "BCGL⠕"] # 0x1d6b6 - - "𝚷": [t: "BCGL⠏"] # 0x1d6b7 - - "𝚸": [t: "BCGL⠗"] # 0x1d6b8 - - "𝚹": [t: "BCGL⠎"] # 0x1d6b9 - - "𝚺": [t: "BCGL⠞"] # 0x1d6ba - - "𝚻": [t: "BCGL⠥"] # 0x1d6bb - - "𝚼": [t: "BCGL⠋"] # 0x1d6bc - - "𝚽": [t: "BCGL⠯"] # 0x1d6bd - - "𝚾": [t: "BCGL⠽"] # 0x1d6be - - "𝚿": [t: "BCGL⠺"] # 0x1d6bf - - - "𝛼": [t: "IGL⠁"] # 0x1d6fc - - "𝛽": [t: "IGL⠃"] # 0x1d6fd - - "𝛾": [t: "IGL⠛"] # 0x1d6fe - - "𝛿": [t: "IGL⠙"] # 0x1d6ff - - "𝜀": [t: "IGL⠑"] # 0x1d700 - - "𝜁": [t: "IGL⠵"] # 0x1d701 - - "𝜂": [t: "IGL⠱ "] # 0x1d702 - - "𝜃": [t: "IGL⠹"] # 0x1d703 - - "𝜄": [t: "IGL⠊"] # 0x1d704 - - "𝜅": [t: "IGL⠅"] # 0x1d705 - - "𝜆": [t: "IGL⠇"] # 0x1d706 - - "𝜇": [t: "IGL⠍"] # 0x1d707 - - "𝜈": [t: "IGL⠝"] # 0x1d708 - - "𝜉": [t: "IGL⠭"] # 0x1d709 - - "𝜊": [t: "IGL⠕"] # 0x1d70a - - "𝜋": [t: "IGL⠏"] # 0x1d70b - - "𝜌": [t: "IGL⠗"] # 0x1d70c - - "𝜍": [t: "IGL⠎"] # 0x1d70d - - "𝜎": [t: "IGL⠞"] # 0x1d70e - - "𝜏": [t: "IGL⠥"] # 0x1d70f - - "𝜐": [t: "IGL⠋"] # 0x1d710 - - "𝜑": [t: "IGL⠯"] # 0x1d711 - - "𝜒": [t: "IGL⠽"] # 0x1d712 - - "𝜓": [t: "IGL⠺"] # 0x1d713 - - - "𝛢": [t: "ICGL⠁"] # 0x1d6e2 - - "𝛣": [t: "ICGL⠃"] # 0x1d6e3 - - "𝛤": [t: "ICGL⠛"] # 0x1d6e4 - - "𝛥": [t: "ICGL⠙"] # 0x1d6e5 - - "𝛦": [t: "ICGL⠑"] # 0x1d6e6 - - "𝛧": [t: "ICGL⠵"] # 0x1d6e7 - - "𝛨": [t: "ICGL⠱ "] # 0x1d6e8 - - "𝛩": [t: "ICGL⠹"] # 0x1d6e9 - - "𝛪": [t: "ICGL⠊"] # 0x1d6ea - - "𝛫": [t: "ICGL⠅"] # 0x1d6eb - - "𝛬": [t: "ICGL⠇"] # 0x1d6ec - - "𝛭": [t: "ICGL⠍"] # 0x1d6ed - - "𝛮": [t: "ICGL⠝"] # 0x1d6ee - - "𝛯": [t: "ICGL⠭"] # 0x1d6ef - - "𝛰": [t: "ICGL⠕"] # 0x1d6f0 - - "𝛱": [t: "ICGL⠏"] # 0x1d6f1 - - "𝛲": [t: "ICGL⠗"] # 0x1d6f2 - - "𝛳": [t: "ICGL⠎"] # 0x1d6f3 - - "𝛴": [t: "ICGL⠞"] # 0x1d6f4 - - "𝛵": [t: "ICGL⠥"] # 0x1d6f5 - - "𝛶": [t: "ICGL⠋"] # 0x1d6f6 - - "𝛷": [t: "ICGL⠯"] # 0x1d6f7 - - "𝛸": [t: "ICGL⠽"] # 0x1d6f8 - - "𝛹": [t: "ICGL⠺"] # 0x1d6f9 - - - "𝜶": [t: "BIGL⠁"] # 0x1d736 - - "𝜷": [t: "BIGL⠃"] # 0x1d737 - - "𝜸": [t: "BIGL⠛"] # 0x1d738 - - "𝜹": [t: "BIGL⠙"] # 0x1d739 - - "𝜺": [t: "BIGL⠑"] # 0x1d73a - - "𝜻": [t: "BIGL⠵"] # 0x1d73b - - "𝜼": [t: "BIGL⠱ "] # 0x1d73c - - "𝜽": [t: "BIGL⠹"] # 0x1d73d - - "𝜾": [t: "BIGL⠊"] # 0x1d73e - - "𝜿": [t: "BIGL⠅"] # 0x1d73f - - "𝝀": [t: "BIGL⠇"] # 0x1d740 - - "𝝁": [t: "BIGL⠍"] # 0x1d741 - - "𝝂": [t: "BIGL⠝"] # 0x1d742 - - "𝝃": [t: "BIGL⠭"] # 0x1d743 - - "𝝄": [t: "BIGL⠕"] # 0x1d744 - - "𝝅": [t: "BIGL⠏"] # 0x1d745 - - "𝝆": [t: "BIGL⠗"] # 0x1d746 - - "𝝇": [t: "BIGL⠎"] # 0x1d747 - - "𝝈": [t: "BIGL⠞"] # 0x1d748 - - "𝝉": [t: "BIGL⠥"] # 0x1d749 - - "𝝊": [t: "BIGL⠋"] # 0x1d74a - - "𝝋": [t: "BIGL⠯"] # 0x1d74b - - "𝝌": [t: "BIGL⠽"] # 0x1d74c - - "𝝍": [t: "BIGL⠺"] # 0x1d74d - - - "𝜜": [t: "BICGL⠁"] # 0x1d71c - - "𝜝": [t: "BICGL⠃"] # 0x1d71d - - "𝜞": [t: "BICGL⠛"] # 0x1d71e - - "𝜟": [t: "BICGL⠙"] # 0x1d71f - - "𝜠": [t: "BICGL⠑"] # 0x1d720 - - "𝜡": [t: "BICGL⠵"] # 0x1d721 - - "𝜢": [t: "BICGL⠱ "] # 0x1d722 - - "𝜣": [t: "BICGL⠹"] # 0x1d723 - - "𝜤": [t: "BICGL⠊"] # 0x1d724 - - "𝜥": [t: "BICGL⠅"] # 0x1d725 - - "𝜦": [t: "BICGL⠇"] # 0x1d726 - - "𝜧": [t: "BICGL⠍"] # 0x1d727 - - "𝜨": [t: "BICGL⠝"] # 0x1d728 - - "𝜩": [t: "BICGL⠭"] # 0x1d729 - - "𝜪": [t: "BICGL⠕"] # 0x1d72a - - "𝜫": [t: "BICGL⠏"] # 0x1d72b - - "𝜬": [t: "BICGL⠗"] # 0x1d72c - - "𝜭": [t: "BICGL⠎"] # 0x1d72d - - "𝜮": [t: "BICGL⠞"] # 0x1d72e - - "𝜯": [t: "BICGL⠥"] # 0x1d72f - - "𝜰": [t: "BICGL⠋"] # 0x1d730 - - "𝜱": [t: "BICGL⠯"] # 0x1d731 - - "𝜲": [t: "BICGL⠽"] # 0x1d732 - - "𝜳": [t: "BICGL⠺"] # 0x1d733 - - - "𝝰": [t: "BSGL⠁"] # 0x1d770 - - "𝝱": [t: "BSGL⠃"] # 0x1d771 - - "𝝲": [t: "BSGL⠛"] # 0x1d772 - - "𝝳": [t: "BSGL⠙"] # 0x1d773 - - "𝝴": [t: "BSGL⠑"] # 0x1d774 - - "𝝵": [t: "BSGL⠵"] # 0x1d775 - - "𝝶": [t: "BSGL⠱ "] # 0x1d776 - - "𝝷": [t: "BSGL⠹"] # 0x1d777 - - "𝝸": [t: "BSGL⠊"] # 0x1d778 - - "𝝹": [t: "BSGL⠅"] # 0x1d779 - - "𝝺": [t: "BSGL⠇"] # 0x1d77a - - "𝝻": [t: "BSGL⠍"] # 0x1d77b - - "𝝼": [t: "BSGL⠝"] # 0x1d77c - - "𝝽": [t: "BSGL⠭"] # 0x1d77d - - "𝝾": [t: "BSGL⠕"] # 0x1d77e - - "𝝿": [t: "BSGL⠏"] # 0x1d77f - - "𝞀": [t: "BSGL⠗"] # 0x1d780 - - "𝞁": [t: "BSGL⠎"] # 0x1d781 - - "𝞂": [t: "BSGL⠞"] # 0x1d782 - - "𝞃": [t: "BSGL⠥"] # 0x1d783 - - "𝞄": [t: "BSGL⠋"] # 0x1d784 - - "𝞅": [t: "BSGL⠯"] # 0x1d785 - - "𝞆": [t: "BSGL⠽"] # 0x1d786 - - "𝞇": [t: "BSGL⠺"] # 0x1d787 - - - "𝝖": [t: "BSCGL⠁"] # 0x1d756 - - "𝝗": [t: "BSCGL⠃"] # 0x1d757 - - "𝝘": [t: "BSCGL⠛"] # 0x1d758 - - "𝝙": [t: "BSCGL⠙"] # 0x1d759 - - "𝝚": [t: "BSCGL⠑"] # 0x1d75a - - "𝝛": [t: "BSCGL⠵"] # 0x1d75b - - "𝝜": [t: "BSCGL⠱ "] # 0x1d75c - - "𝝝": [t: "BSCGL⠹"] # 0x1d75d - - "𝝞": [t: "BSCGL⠊"] # 0x1d75e - - "𝝟": [t: "BSCGL⠅"] # 0x1d75f - - "𝝠": [t: "BSCGL⠇"] # 0x1d760 - - "𝝡": [t: "BSCGL⠍"] # 0x1d761 - - "𝝢": [t: "BSCGL⠝"] # 0x1d762 - - "𝝣": [t: "BSCGL⠭"] # 0x1d763 - - "𝝤": [t: "BSCGL⠕"] # 0x1d764 - - "𝝥": [t: "BSCGL⠏"] # 0x1d765 - - "𝝦": [t: "BSCGL⠗"] # 0x1d766 - - "𝝧": [t: "BSCGL⠎"] # 0x1d767 - - "𝝨": [t: "BSCGL⠞"] # 0x1d768 - - "𝝩": [t: "BSCGL⠥"] # 0x1d769 - - "𝝪": [t: "BSCGL⠋"] # 0x1d76a - - "𝝫": [t: "BSCGL⠯"] # 0x1d76b - - "𝝬": [t: "BSCGL⠽"] # 0x1d76c - - "𝝭": [t: "BSCGL⠺"] # 0x1d76d - - - "𝞪": [t: "BISGL⠁"] # 0x1d7aa - - "𝞫": [t: "BISGL⠃"] # 0x1d7ab - - "𝞬": [t: "BISGL⠛"] # 0x1d7ac - - "𝞭": [t: "BISGL⠙"] # 0x1d7ad - - "𝞮": [t: "BISGL⠑"] # 0x1d7ae - - "𝞯": [t: "BISGL⠵"] # 0x1d7af - - "𝞰": [t: "BISGL⠱ "] # 0x1d7b0 - - "𝞱": [t: "BISGL⠹"] # 0x1d7b1 - - "𝞲": [t: "BISGL⠊"] # 0x1d7b2 - - "𝞳": [t: "BISGL⠅"] # 0x1d7b3 - - "𝞴": [t: "BISGL⠇"] # 0x1d7b4 - - "𝞵": [t: "BISGL⠍"] # 0x1d7b5 - - "𝞶": [t: "BISGL⠝"] # 0x1d7b6 - - "𝞷": [t: "BISGL⠭"] # 0x1d7b7 - - "𝞸": [t: "BISGL⠕"] # 0x1d7b8 - - "𝞹": [t: "BISGL⠏"] # 0x1d7b9 - - "𝞺": [t: "BISGL⠗"] # 0x1d7ba - - "𝞻": [t: "BISGL⠎"] # 0x1d7bb - - "𝞼": [t: "BISGL⠞"] # 0x1d7bc - - "𝞽": [t: "BISGL⠥"] # 0x1d7bd - - "𝞾": [t: "BISGL⠋"] # 0x1d7be - - "𝞿": [t: "BISGL⠯"] # 0x1d7bf - - "𝟀": [t: "BISGL⠽"] # 0x1d7c0 - - "𝟁": [t: "BISGL⠺"] # 0x1d7c1 - - - "𝞐": [t: "BISCGL⠁"] # 0x1d790 - - "𝞑": [t: "BISCGL⠃"] # 0x1d791 - - "𝞒": [t: "BISCGL⠛"] # 0x1d792 - - "𝞓": [t: "BISCGL⠙"] # 0x1d793 - - "𝞔": [t: "BISCGL⠑"] # 0x1d794 - - "𝞕": [t: "BISCGL⠵"] # 0x1d795 - - "𝞖": [t: "BISCGL⠱ "] # 0x1d796 - - "𝞗": [t: "BISCGL⠹"] # 0x1d797 - - "𝞘": [t: "BISCGL⠊"] # 0x1d798 - - "𝞙": [t: "BISCGL⠅"] # 0x1d799 - - "𝞚": [t: "BISCGL⠇"] # 0x1d79a - - "𝞛": [t: "BISCGL⠍"] # 0x1d79b - - "𝞜": [t: "BISCGL⠝"] # 0x1d79c - - "𝞝": [t: "BISCGL⠭"] # 0x1d79d - - "𝞞": [t: "BISCGL⠕"] # 0x1d79e - - "𝞟": [t: "BISCGL⠏"] # 0x1d79f - - "𝞠": [t: "BISCGL⠗"] # 0x1d7a0 - - "𝞡": [t: "BISCGL⠎"] # 0x1d7a1 - - "𝞢": [t: "BISCGL⠞"] # 0x1d7a2 - - "𝞣": [t: "BISCGL⠥"] # 0x1d7a3 - - "𝞤": [t: "BISCGL⠋"] # 0x1d7a4 - - "𝞥": [t: "BISCGL⠯"] # 0x1d7a5 - - "𝞦": [t: "BISCGL⠽"] # 0x1d7a6 - - "𝞧": [t: "BISCGL⠺"] # 0x1d7a7 - + - "𝕊-𝕐": # 0x1d54a - 0x1d550 + - tc: "𝔹" + - spell: "translate('.', '𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'STUVWXY')" + - "𝕒-𝕫": # 0x1d552 - 0x1d56b + - tc: "𝔹" + - spell: "translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz')" + - "𝕬-𝖅": # 0x1d56c - 0x1d585 + - tc: "BD" + - spell: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝖆-𝖟": # 0x1d586 - 0x1d59f + - tc: "BD" + - spell: "translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz')" + - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 + - tc: "S" + - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝖺-𝗓": # 0x1d5ba - 0x1d5d3 + - tc: "S" + - spell: "translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz')" + - "𝗔-𝗭": # 0x1d5d4 - 0x1d5ed + - tc: "BS" + - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝗮-𝘇": # 0x1d5ee - 0x1d607 + - tc: "BS" + - spell: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" + - "𝘈-𝘡": # 0x1d608 - 0x1d621 + - tc: "IS" + - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝘢-𝘻": # 0x1d622 - 0x1d63b + - tc: "IS" + - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" + - "𝘼-𝙕": # 0x1d63c - 0x1d655 + - tc: "BIS" + - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝙖-𝙯": # 0x1d656 - 0x1d66f + - tc: "BIS" + - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" + - "𝙰-𝚉": # 0x1d670 - 0x1d689 + - tc: "" + - spell: "translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝚊-𝚥": # 0x1d68a - 0x1d6a5 + - tc: "" + - spell: "translate('.', '𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣𝚤𝚥', 'abcdefghijklmnopqrstuvwxyzij')" + + # Mathematical Greek alphabets (typeface via tc:; G/C from base Greek in unicode.yaml) + - "𝛂-𝛙": # 0x1d6c2 - 0x1d6d9 + - tc: "B" + - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒𝛓𝛔𝛕𝛖𝛗𝛘𝛙', 'αβγδεζηθικλμνξοπρςστυφχψ')" + - "𝚨-𝚿": # 0x1d6a8 - 0x1d6bf + - tc: "B" + - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨ')" + - "𝛼-𝜓": # 0x1d6fc - 0x1d713 + - tc: "I" + - spell: "translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌𝜍𝜎𝜏𝜐𝜑𝜒𝜓', 'αβγδεζηθικλμνξοπρςστυφχψ')" + - "𝛢-𝛹": # 0x1d6e2 - 0x1d6f9 + - tc: "I" + - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨ')" + - "𝜶-𝝍": # 0x1d736 - 0x1d74d + - tc: "BI" + - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆𝝇𝝈𝝉𝝊𝝋𝝌𝝍', 'αβγδεζηθικλμνξοπρςστυφχψ')" + - "𝜜-𝜳": # 0x1d71c - 0x1d733 + - tc: "BI" + - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨ')" + - "𝝰-𝞇": # 0x1d770 - 0x1d787 + - tc: "BS" + - spell: "translate('.', '𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿𝞀𝞁𝞂𝞃𝞄𝞅𝞆𝞇', 'αβγδεζηθικλμνξοπρςστυφχψ')" + - "𝝖-𝝭": # 0x1d756 - 0x1d76d + - tc: "BS" + - spell: "translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨ')" + - "𝞪-𝟁": # 0x1d7aa - 0x1d7c1 + - tc: "BIS" + - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺𝞻𝞼𝞽𝞾𝞿𝟀𝟁', 'αβγδεζηθικλμνξοπρςστυφχψ')" + - "𝞐-𝞧": # 0x1d790 - 0x1d7a7 + - tc: "BIS" + - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨ')" + + + # Mathematical digits - "𝟎-𝟗": # 0x1d7ce - 0x1d7d7 - - t: "B" - - spell: "translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789')" - - - "-": # 0xf52e - 0xf537 (old MathType) - - t: "B" - - spell: "translate('.', '', '0123456789')" - - - "𝟬-𝟵": # 0x1D7EC - 0x1D7F5 - - t: "B" - - spell: "translate('.', '𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵', '0123456789')" - - - "-": # 0xf556 - 0xf55f (old MathType) - - t: "B" - - spell: "translate('.', '', '0123456789')" - - - "𝟢-𝟫": # 0x1d7e2 - 0x1d7eb - - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" - - - "𝟶-𝟿": # 0x1d7f6 - 0x1d7ff - - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" + - tc: "B" + - spell: "translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789')" + - "𝟘-𝟡": # 0x1d7d8 - 0x1d7e1 + - tc: "𝔹" + - spell: "translate('.', '𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡', '0123456789')" + - "𝟢-𝟫": # 0x1d7e2 - 0x1d7eb + - tc: "S" + - spell: "translate('.', '𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫', '0123456789')" + - "𝟬-𝟵": # 0x1d7ec - 0x1d7f5 + - tc: "BS" + - spell: "translate('.', '𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵', '0123456789')" + - "𝟶-𝟿": # 0x1d7f6 - 0x1d7ff + - tc: "" + - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" + - "-": # 0xf54c - 0xf555 (old MathType bold) + - tc: "B" + - spell: "translate('.', '', '0123456789')" + - "-": # 0xf556 - 0xf55f (old MathType bold) + - tc: "B" + - spell: "translate('.', '', '0123456789')" diff --git a/Rules/Braille/French/unicode.yaml b/Rules/Braille/French/unicode.yaml index f7e23239b..ce310bb3c 100644 --- a/Rules/Braille/French/unicode.yaml +++ b/Rules/Braille/French/unicode.yaml @@ -231,7 +231,7 @@ - "8": [t: "N⠳"] # 0x0038 (digit eight) - "9": [t: "N⠪"] # 0x0039 (digit nine) - ":": [t: "⠒"] # 0x003A (colon) - - ";": [t: "⠆"] # 0x003B (semicolon) + - ";": [t: ";"] # 0x003B (semicolon) - "<": [t: "⠐⠣"] # 0x003C (less than sign) - "=": [t: "⠶"] # 0x003D (regular equals sign) - ">": [t: "⠐⠜"] # 0x003E (greater than sign ( regular )) @@ -262,15 +262,14 @@ - "¢": [t: "⠉"] # 0x00A2 (cent) - "¯": [t: "⠸⠒"] # 0x00AF (bar) - "°": [t: "⠐⠕"] # 0x00B0 (degree sign) + - "℃": [t: "⠐⠕⠨⠉"] # 0x2103 (degree Celsius) + - "℉": [t: "⠐⠕⠨⠋"] # 0x2109 (degree Fahrenheit) - "±": [t: "⠖⠤"] # 0x00B1 (plus or minus) - "¼": [t: "⠡⠌⠹"] # 0x00BC (vulgar fraction one quarter) - "½": [t: "⠡⠌⠣"] # 0x00BD (vulgar fraction one half) - "¾": [t: "⠩⠌⠹"] # 0x00BE (vulgar fraction three quarters) - "×": [t: "⠔"] # 0x00D7 ( multiplication sign) - "÷": [t: "⠒"] # 0x00F7 (division sign ( divided by )) - - "ב": [t: "⠘⠘⠃"] # 0x05D1 ( beth) - - "ג": [t: "⠘⠘⠛"] # 0x05D2 (gimel) - - "ד": [t: "⠘⠘⠙"] # 0x05D3 (daleth) - "◌̌": [t: "⠸⠘⠒"] # 0x030C (combing caron) - "‖": [t: "⠘⠿"] # 0x2016 (double vertical bar) - "‰": [t: "⠐⠬⠬"] # 0x2030 (per mille sign) @@ -279,9 +278,13 @@ - "‴": [t: "⠄⠄⠄"] # 0x2034 (triple prime) - "€": [t: "⠘⠑"] # 0x20AC (euro) - "Å": [t: "⠨⠁⠈⠕"] # 0x212B (angstrom) - - "ℵ": [t: "⠘⠘⠁"] # 0x2135 (alef) + - "ℵ": [t: "HL⠁"] # 0x2135 (alef) - "←": [t: "⠸⠪"] # 0x2190 (left pointing arrow) - - "↑": [t: "⠘⠻"] # 0x2191 (arrow pointing up) + - "↑": # 0x2191 (arrow pointing up) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠘⠱"] + else: [t: "⠘⠻"] - "→": # 0x2192 (right pointing arrow) - test: if: "parent::m:mover and not(following-sibling::*)" @@ -292,8 +295,11 @@ if: "parent::m:mover and not(following-sibling::*)" then: [t: "⠨⠒"] else: [t: "⠸⠱"] - - - "↓": [t: "⠨⠻"] # 0x2193 (arrow pointing down) + - "↓": # 0x2193 (arrow pointing down) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠨⠱"] + else: [t: "⠨⠻"] - "↔": [t: "⠐⠻"] # 0x2194 (horizontal two-way arrow) - "↖": [t: "⠘⠪"] # 0x2196 (north west arrow) - "↗": [t: "⠘⠱"] # 0x2197 (north east arrow) @@ -340,7 +346,12 @@ - "≈": [t: "⠐⠶"] # 0x2248 (double tilde) - "≙": [t: "⠒⠶"] # 0x2259 (caret over equals sign) - "≠": [t: "⠨⠶"] # 0x2260 (not equal to) - - "≡": [t: "⠶⠶"] # 0x2261 (identify (is congruent to, is identical with)) + - "≡": # 0x2261 (identify (is congruent to, is identical with)) + - test: + if: "@data-chemical-bond" + then: [t: "⠿"] + else: [t: "⠶⠶"] + - "≢": [t: "⠨⠶⠶"] # 0x2262 (is not identical to) - "≤": [t: "⠘⠣"] # 0x2264 (bar under less than sign (is less than or equal to)) - "≥": [t: "⠘⠜"] # 0x2265 (bar under greater than sign (is greater than or equal to)) @@ -360,6 +371,17 @@ - "⊗": [t: "⠨⠔"] # 0x2297 (circle with interior cross (tensor product)) - "⊥": [t: "⠘⠳"] # 0x22A5 (is perpendicular to) - "⋅": [t: "⠔⠔"] # 0x22C5 (dot,and times) + - "⟵": [t: "⠸⠪"] # 0x27F5 (Long leftwards arrow ) + - "⟶": # 0x27F6 (Long rightwards arrow) + - test: + if: "parent::m:mover and not(following-sibling::*)" + then: [t: "⠨⠒"] + else: [t: "⠸⠱"] + - "⟷": [t: "⠸⠻"] # 0x27F7 (Long left right arrow) + - "⟸": [t: "⠐⠒"] # 0x27F8 (Long leftwards arrow ) + - "⟹": [t: "⠒⠂"] # 0x27F9 (Long rightwards arrow) + - "⟺": [t: "⠐⠒⠂"] # 0x27FA (Long left right arrow) + - "⌄": [t: "⠸⠘⠒"] # 0x2304 (inverted caret) - "⌣": [t: "⠈⠈⠈"] # 0x2323 (smile) - "⏜": [t: "⠈⠒"] # 0x23DC (arc concave upward) diff --git a/tests/braille.rs b/tests/braille.rs index 43afe893a..1a0c68b40 100644 --- a/tests/braille.rs +++ b/tests/braille.rs @@ -41,6 +41,7 @@ mod braille { mod French { mod french; mod chemistry; + mod other; } } diff --git a/tests/braille/French/chemistry.rs b/tests/braille/French/chemistry.rs index 4eedcda3a..97528e114 100644 --- a/tests/braille/French/chemistry.rs +++ b/tests/braille/French/chemistry.rs @@ -1,9 +1,69 @@ -// French braille tests for the basic mathml tags -// These tests are from the French braille authority's publication "NOTATION PIATHÉMATIQUE BRAILLE" (Première édition janvier 2007) -// https://www.avh.asso.fr/sites/default/files/notation_mathematique_braille2_0.pdf +// French braille tests for chemistry +// From "Notation braille dans le domaine de la chimie" (Première édition juin 2008) +// BrailleDocs/notation_braille_chimie2008.pdf use crate::common::*; use anyhow::Result; +// --- I. Atomic and molecular writing --- + +#[test] +fn atom_I_1_1() -> Result<()> { + let expr = r#"H"#; + test_braille("French", expr, "⠨⠓")?; + return Ok(()); +} + +#[test] +fn atom_I_1_2() -> Result<()> { + let expr = r#"Al"#; + test_braille("French", expr, "⠨⠁⠇")?; + return Ok(()); +} + +#[test] +fn isotope_I_2_1() -> Result<()> { + // ¹⁶₈O + let expr = r#" + O + + 8 + 16 + "#; + test_braille("French", expr, "⠨⠕⠠⠢⠳⠠⠈⠡⠫")?; + return Ok(()); +} + +#[test] +#[ignore = "average-mass left subscript not handled by French atomic-number rule"] +fn isotope_I_2_2() -> Result<()> { + // ²⁵₂₅⁵⁴.⁹Mn (Z=25, average mass 54.9, A=25) + let expr = r#" + Mn + + 25 + 25 + 54.9 + + "#; + test_braille("French", expr, "⠨⠍⠝⠠⠢⠣⠱⠠⠢⠢⠱⠹⠂⠪⠠⠈⠱⠱")?; + return Ok(()); +} + +#[test] +fn molecule_I_3_1() -> Result<()> { + let expr = r#"H2O"#; + test_braille("French", expr, "⠨⠓⠣⠨⠕")?; + return Ok(()); +} + +#[test] +fn molecule_I_3_2() -> Result<()> { + // Cu(NO₃)₂ + let expr = r#"Cu(NO3)2"#; + test_braille("French", expr, "⠨⠉⠥⠦⠨⠝⠨⠕⠩⠴⠣")?; + return Ok(()); +} + #[test] fn bond_I_4_1() -> Result<()> { let expr= r#"H-H"#; @@ -18,15 +78,177 @@ fn bond_I_4_2() -> Result<()> { return Ok(()); } +#[test] +fn bond_I_4_3() -> Result<()> { + let expr = r#"N≡N"#; + test_braille("French", expr, "⠨⠝⠿⠨⠝")?; + return Ok(()); +} + +#[test] +fn lewis_I_5_1() -> Result<()> { + // O with 2 Lewis doublets (one bar above, one below) → ⠨⠕⠰⠣⠙⠆ + let expr = r#" + O―― + "#; + test_braille("French", expr, "⠨⠕⠰⠣⠙⠆")?; + return Ok(()); +} + +#[test] +fn lewis_I_5_2() -> Result<()> { + // H−O−H with 2 doublets on O + let expr = r#"H- + O―― + - H"#; + test_braille("French", expr, "⠨⠓⠤⠨⠕⠰⠣⠙⠆⠤⠨⠓")?; + return Ok(()); +} + +#[test] +#[ignore = "Lewis notation with bars on left/right of an atom not implemented"] +fn lewis_I_5_3() -> Result<()> { + // H−Cl with 3 doublets on Cl + let expr = r#"H- + Cl―― + |"#; + test_braille("French", expr, "⠨⠓⠤⠨⠉⠇⠰⠩⠙⠆")?; + return Ok(()); +} + +// --- II. Ionic writing --- + +#[test] +fn ion_II_1_1() -> Result<()> { + let expr = r#"H+"#; + test_braille("French", expr, "⠨⠓⠈⠖")?; + return Ok(()); +} + +#[test] +fn ion_II_1_2() -> Result<()> { + let expr = r#"O2-"#; + test_braille("French", expr, "⠨⠕⠈⠰⠣⠤⠆")?; + return Ok(()); +} + #[test] fn ion_II_1_3() -> Result<()> { let expr= r#"SO42-"#; - test_braille("French", expr, "⠨⠎⠨⠕⠢⠹⠈⠰⠣⠤⠆")?; + test_braille("French", expr, "⠨⠎⠨⠕⠹⠈⠰⠣⠤⠆")?; + return Ok(()); +} + +#[test] +fn dissociation_II_2_1() -> Result<()> { + // (Cu²⁺; SO₄²⁻) + let expr = r#"( + Cu2+; + SO42- + )"#; + test_braille("French", expr, "⠦⠨⠉⠥⠈⠰⠣⠖⠆⠠⠆⠨⠎⠨⠕⠹⠈⠰⠣⠤⠆⠴")?; + return Ok(()); +} + +#[test] +fn dipole_II_3_1() -> Result<()> { + // Hᵟ⁺ − Clᵟ⁻ + let expr = r#" + Hδ+ + - + Clδ- + "#; + test_braille("French", expr, "⠨⠓⠈⠰⠘⠙⠈⠖⠆⠤⠨⠉⠇⠈⠰⠘⠙⠈⠤⠆")?; + return Ok(()); +} + +// --- III. Chemical reactions and transformations --- + +#[test] +fn reaction_III_2_1() -> Result<()> { + // C(s)+O₂(g)→CO₂(g) + let expr = r#" + C(s) + + + O2(g) + → + CO2(g) + "#; + test_braille("French", expr, "⠨⠉⠦⠎⠴⠖⠨⠕⠣⠦⠛⠴⠸⠱⠨⠉⠨⠕⠣⠦⠛⠴")?; + return Ok(()); +} + +#[test] +fn reaction_III_2_2() -> Result<()> { + // CO(g)+H₂O(l)→CO₂(g)+H₂(g) — line-continuation ⠐ omitted (single-line MathML) + let expr = r#" + CO(g) + + + H2O(l) + → + CO2(g) + + + H2(g) + "#; + test_braille("French", expr, "⠨⠉⠨⠕⠦⠛⠴⠖⠨⠓⠣⠨⠕⠦⠇⠴⠸⠱⠨⠉⠨⠕⠣⠦⠛⠴⠖⠨⠓⠣⠦⠛⠴")?; + return Ok(()); +} + +#[test] +fn reaction_III_2_3() -> Result<()> { + // HCl(g)+H₂O(l)→(eau) H₃O⁺(aq)+Cl⁻(aq) — line-continuation ⠐ omitted + let expr = r#" + HCl(g) + + + H2O(l) + →(eau) + H3O+ + (aq) + + + Cl- + (aq) + "#; + test_braille("French", expr, "⠨⠓⠨⠉⠇⠦⠛⠴⠖⠨⠓⠣⠨⠕⠦⠇⠴⠸⠱⠦⠑⠁⠥⠴⠨⠓⠩⠨⠕⠈⠖⠦⠁⠟⠴⠖⠨⠉⠇⠈⠤⠦⠁⠟⠴")?; + return Ok(()); +} + +#[test] +fn emerges_III_3_1() -> Result<()> { + // 2H₃O⁺ + FeS → Fe²⁺ + 2H₂O + H₂S↑ + let expr = r#" + 2H3O+ + + + FeS + → + Fe2+ + + + 2H2O + + + H2S↑ + "#; + test_braille("French", expr, "⠣⠨⠓⠩⠨⠕⠈⠰⠖⠆⠖⠨⠋⠑⠨⠎⠸⠱⠨⠋⠑⠈⠰⠣⠖⠆⠖⠣⠨⠓⠣⠨⠕⠖⠨⠓⠣⠨⠎⠘⠱")?; + return Ok(()); +} + +#[test] +fn precipitate_III_4_1() -> Result<()> { + // H₂S(g)+Cl₂(g)→2HCl(g)+S(s)↓ + let expr = r#" + H2S(g) + + + Cl2(g) + → + 2HCl(g) + + + S(s)↓ + "#; + test_braille("French", expr, "⠨⠓⠣⠨⠎⠦⠛⠴⠖⠨⠉⠇⠣⠦⠛⠴⠸⠱⠣⠨⠓⠨⠉⠇⠦⠛⠴⠖⠨⠎⠦⠎⠴⠨⠱")?; return Ok(()); } #[test] fn equilibrium_III_5_1() -> Result<()> { + // N₂(g)+3H₂(g)⇌2NH₃(g) let expr= r#"N2(g)+3H2(g)⇌2NH3(g)"#; test_braille("French", expr, "⠨⠝⠣⠦⠛⠴⠖⠩⠨⠓⠣⠦⠛⠴⠸⠻⠣⠨⠝⠨⠓⠩⠦⠛⠴")?; return Ok(()); @@ -34,10 +256,12 @@ fn equilibrium_III_5_1() -> Result<()> { #[test] fn equilibrium_III_5_2() -> Result<()> { + // 2H₂O ⇌(1;2) H₃O⁺ + OH⁻ (line-continuation ⠐ omitted) + // Note: braille doesn't match the example -- no parens around the '1' and '2' let expr= r#"2H2O - ⇌(2)(1)H3O++OH-"#; - test_braille("French", expr, "⠣⠨⠓⠣⠨⠕⠰⠸⠻⠆⠢⠢⠦⠣⠴⠈⠈⠦⠡⠴⠨⠓⠩⠨⠕⠈⠰⠖⠆⠖⠨⠕⠨⠓⠈⠰⠤⠆")?; + ⇌21 + H3O++OH-"#; + test_braille("French", expr, "⠣⠨⠓⠣⠨⠕⠸⠻⠦⠡⠆⠣⠴⠨⠓⠩⠨⠕⠈⠰⠖⠆⠖⠨⠕⠨⠓⠈⠤")?; return Ok(()); } @@ -49,6 +273,68 @@ fn equilibrium_III_5_3() -> Result<()> { R-CO-O- R'+ H2O"#; - test_braille("French", expr, "⠨⠗⠤⠨⠉⠨⠕⠤⠨⠕⠤⠨⠓⠖⠨⠗⠄⠤⠨⠕⠨⠓ ⠸⠻⠨⠢⠰⠓⠽⠙⠗⠕⠇⠽⠎⠑⠆⠨⠔⠈⠰⠑⠎⠞⠗⠊⠋⠊⠉⠁⠞⠊⠕⠝⠆⠨⠗⠤⠨⠉⠨⠕⠤⠨⠕⠤⠨⠗⠄⠖⠨⠓⠢⠣⠨⠕")?; + test_braille("French", expr, "⠨⠗⠤⠨⠉⠨⠕⠤⠨⠕⠤⠨⠓⠖⠨⠗⠄⠤⠨⠕⠨⠓⠸⠻⠦⠑⠎⠞⠿⠗⠊⠋⠊⠉⠁⠞⠊⠕⠝⠆⠓⠽⠙⠗⠕⠇⠽⠎⠑⠴⠨⠗⠤⠨⠉⠨⠕⠤⠨⠕⠤⠨⠗⠄⠖⠨⠓⠣⠨⠕")?; + return Ok(()); +} + +#[test] +fn equilibrium_III_5_4() -> Result<()> { + // A ⇌ B with hν / empty half-zone ⠐⠂ + let expr = r#" + A + ⇌hν + B + "#; + test_braille("French", expr, "⠨⠁⠸⠻⠦⠓⠘⠝⠆⠐⠂⠴⠨⠃")?; + return Ok(()); +} + +// --- IV. Some notes (redox pairs / mesomerism) --- + +#[test] +fn redox_IV_1_1() -> Result<()> { + let expr = r#" + Cu2+ + / + Cu + "#; + test_braille("French", expr, "⠨⠉⠥⠈⠰⠣⠖⠆⠌⠨⠉⠥")?; + return Ok(()); +} + +#[test] +fn redox_IV_1_2() -> Result<()> { + let expr = r#" + Fe3+ + / + Fe2+ + "#; + test_braille("French", expr, "⠨⠋⠑⠈⠰⠩⠖⠆⠌⠨⠋⠑⠈⠰⠣⠖⠆")?; + return Ok(()); +} + +#[test] +fn redox_IV_1_3() -> Result<()> { + let expr = r#" + H3O+ + / + H2O + "#; + test_braille("French", expr, "⠨⠓⠩⠨⠕⠈⠖⠌⠨⠓⠣⠨⠕")?; + return Ok(()); +} + +#[test] +fn mesomerism_IV_2_1() -> Result<()> { + // (CH₃)₂C=O ↔ (CH₃)₂C⁺−O⁻ + let expr = r#" + (CH3)2 + C=O + ↔ + (CH3)2 + C+- + O- + "#; + test_braille("French", expr, "⠦⠨⠉⠨⠓⠩⠴⠣⠨⠉⠶⠨⠕⠐⠻⠦⠨⠉⠨⠓⠩⠴⠣⠨⠉⠈⠖⠤⠨⠕⠈⠤")?; return Ok(()); } diff --git a/tests/braille/French/french.rs b/tests/braille/French/french.rs index a799b8ed7..6b1accc72 100644 --- a/tests/braille/French/french.rs +++ b/tests/braille/French/french.rs @@ -369,8 +369,17 @@ fn square_formula_3_10_05() -> Result<()> { #[test] fn function_from_r_to_r_11_01() -> Result<()> { - let expr= r#"f:ℝ→ℝ,x↦y=1x"#; - test_braille("French", expr, "⠋⠒⠨⠨⠗⠸⠱⠨⠨⠗⠭⠐⠱⠽⠶⠡⠌⠭")?; + // Spec §11 example, first line: f : ℝ → ℝ + let expr= r#"f:ℝ→ℝ"#; + test_braille("French", expr, "⠋⠒⠨⠨⠗⠸⠱⠨⠨⠗")?; + return Ok(()); +} + +#[test] +fn maps_to_y_equals_1_over_x_11_02() -> Result<()> { + // Spec §11 example, second line: x ↦ y = 1/x + let expr= r#"x↦y=1x"#; + test_braille("French", expr, "⠭⠐⠱⠽⠶⠡⠌⠭")?; return Ok(()); } @@ -460,25 +469,39 @@ fn restriction_of_f_to_13_2_04() -> Result<()> { // #[test] // fn determinant_tabular_13_3_01() -> Result<()> { -// let expr= r#"I=|1023|=3"#; +// let expr= r#"I=| +// 10 +// 23 +// |=3"#; // test_braille("French", expr, "not correct⠨⠊⠶⠿⠡⠀⠼⠿ ⠿⠣⠀⠩⠿⠶⠩")?; // return Ok(()); // } #[test] fn matrix_linear_13_3_02() -> Result<()> { - let expr= r#"J=(142356−789)"#; + let expr= r#"J=( + + 14 + 2356 + −789 + )"#; test_braille("French", expr, "⠨⠚⠶⠘⠦⠡⠀⠐⠂⠀⠐⠂⠀⠹⠀⠠⠜⠀⠣⠀⠩⠀⠱⠀⠫⠀⠠⠜⠀⠤⠻⠀⠳⠀⠐⠂⠀⠪⠘⠴")?; return Ok(()); } #[test] fn matrix_with_equations_one_lines_13_3_03() -> Result<()> { - let expr= r#"K=[1x2+3x−96x3−x2+1x+3y+5g(x)]"#; + let expr= r#"K=[ + 1x2+3x−96x3−x2+1 + x+3y+5g(x) + ]"#; test_braille("French", expr, "⠨⠅⠶⠘⠷⠡⠀⠰⠭⠈⠣⠖⠩⠭⠤⠪⠆⠌⠰⠫⠭⠈⠩⠤⠭⠈⠣⠖⠡⠆⠀⠠⠜⠀⠰⠭⠖⠩⠆⠌⠰⠽⠖⠱⠆⠀⠛⠦⠭⠴⠘⠾")?; return Ok(()); } #[test] fn sytem_equation_linéaraire_13_4_01() -> Result<()> { - let expr= r#"{2x+3y=53x−7y=8"#; + let expr= r#"{ + 2x+3y=5 + 3x−7y=8 + "#; test_braille("French", expr, "⠸⠦⠣⠭⠖⠩⠽⠶⠱⠀⠠⠜⠀⠩⠭⠤⠻⠽⠶⠳")?; return Ok(()); } @@ -519,7 +542,10 @@ fn limit_x_tends_1_14_2_01_corrected() -> Result<()> { #[test] fn limit_x_tends_and_x_lt_1_14_2_02_corrected_1() -> Result<()> { - let expr= r#"limx→4x>4f(x)=−∞"#; + let expr= r#"lim + x→4 + x>4 + f(x)=−∞"#; test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠹⠂⠭⠐⠜⠹⠆⠋⠦⠭⠴⠶⠤⠘⠉")?; return Ok(()); } @@ -594,8 +620,12 @@ fn integral_over_curve_14_4_04() -> Result<()> { #[test] fn integral_double_over_surface_14_4_05() -> Result<()> { - let expr= r#"∯Sf(x,y)dxdy"#; - test_braille("French", expr, "⠨⠯⠯⠢⠨⠎⠋⠦⠭⠂⠽⠴⠙⠭⠙⠽")?; + let expr= r#"∯S + f(x,y)dxdy"#; + // The original expr doesn't bracket bracket the integral, but other similar cases do bracket an exponent that is followed by a char + // I was told that leaning towards disambiguation is better, and was told MathCAT's version was OK + // test_braille("French", expr, "⠨⠯⠯⠢⠨⠎⠋⠦⠭⠂⠽⠴⠙⠭⠙⠽")?; + test_braille("French", expr, "⠰⠨⠯⠯⠢⠨⠎⠆⠋⠦⠭⠂⠽⠴⠙⠭⠙⠽")?; return Ok(()); } @@ -633,8 +663,18 @@ fn cos_of_sum_2_14_6_01b() -> Result<()> { test_braille_prefs("French", vec![("French_UseShortForm", "true")], expr, "⠨⠬⠦⠁⠖⠃⠴⠶⠨⠬⠁⠨⠬⠃⠤⠬⠁⠬⠃")?; return Ok(()); } + #[test] fn cosh_sinh_14_7_01() -> Result<()> { + let expr= r#"ch2x−sh2x=1"#; + test_braille_prefs("French", vec![("French_UseShortForm", "true")], expr, "⠉⠓⠈⠣⠰⠭⠆⠤⠎⠓⠈⠣⠰⠭⠆⠶⠡")?; + + return Ok(()); +} + + +#[test] +fn cosh_sinh_14_7_01_fullname() -> Result<()> { let expr= r#"cosh2x−sinh2x=1"#; test_braille_prefs("French", vec![("French_UseShortForm", "true")], expr, "⠉⠓⠈⠣⠰⠭⠆⠤⠎⠓⠈⠣⠰⠭⠆⠶⠡")?; @@ -650,10 +690,215 @@ fn exists_element_of_15_01() -> Result<()> { #[test] fn set_formula_15_02() -> Result<()> { - let expr= r#"∀ε>0∃η>0:|x2−x1|≤η⇒|f(x2)−f(x1)|≤ε"#; + let expr= r#"∀ε>0∃ + η>0: + |x2−x1| + ≤η⇒ + |f(x2) + −f(x1) + |≤ε"#; + test_braille("French", expr, "⠸⠌⠘⠑⠐⠜⠼⠀⠸⠡⠘⠓⠐⠜⠼⠒⠀⠿⠭⠢⠣⠤⠭⠢⠡⠿⠘⠣⠘⠓⠀⠒⠂⠀⠿⠋⠦⠭⠢⠣⠴⠤⠋⠦⠭⠢⠡⠴⠿⠘⠣⠘⠑")?; + return Ok(()); +} + +#[test] +fn set_formula_15_02_from_word() -> Result<()> { + // Note: orginal used U+2008 (punctuation space) before ":" which triggers whitespace in the output + // If looks wrong also. Switched to U+2009 (thin space) + let expr= r#" + ∀ + ε + > + 0 +   + ∃ + η + > + 0 +   + : +   + + | + + x2 + - + x1 + + | + + ≤ + η +   + ⟹ +   + + | + + f + + ( + + x2 + + ) + + - + f + + ( + + x1 + + ) + + + | + + ≤ + ε + "#; test_braille("French", expr, "⠸⠌⠘⠑⠐⠜⠼⠀⠸⠡⠘⠓⠐⠜⠼⠒⠀⠿⠭⠢⠣⠤⠭⠢⠡⠿⠘⠣⠘⠓⠀⠒⠂⠀⠿⠋⠦⠭⠢⠣⠴⠤⠋⠦⠭⠢⠡⠴⠿⠘⠣⠘⠑")?; return Ok(()); } + +// --- 16.3 Unités (braille intégral) --- +// Spec also lists braille abrégé (letter indicator ⠠ before unit abbreviations after numbers); +// MathCAT emits intégral math braille only. + +#[test] +fn units_5_3_km_16_3_01() -> Result<()> { + let expr = r#"5,3 km"#; + test_braille("French", expr, "⠱⠂⠩⠀⠅⠍")?; + return Ok(()); +} + +#[test] +fn units_3_40_euro_16_3_02() -> Result<()> { + let expr = r#"3,40 €"#; + test_braille("French", expr, "⠩⠂⠹⠼⠀⠘⠑")?; + return Ok(()); +} + +#[test] +fn units_1_5_kW_16_3_03() -> Result<()> { + let expr = r#"1,5 kW"#; + test_braille("French", expr, "⠡⠂⠱⠀⠅⠨⠺")?; + return Ok(()); +} + +#[test] +fn units_220_V_16_3_04() -> Result<()> { + let expr = r#"220 V"#; + test_braille("French", expr, "⠣⠣⠼⠀⠨⠧")?; + return Ok(()); +} + +#[test] +fn units_67_km2_16_3_05() -> Result<()> { + let expr = r#"67 km2"#; + test_braille("French", expr, "⠫⠻⠀⠅⠍⠈⠣")?; + return Ok(()); +} + +#[test] +fn units_37_deg_16_3_06() -> Result<()> { + // Degree is attached to the number (no space), as in print + let expr = r#"37°"#; + test_braille("French", expr, "⠩⠻⠐⠕")?; + return Ok(()); +} + +#[test] +fn units_dms_16_3_07() -> Result<()> { + // 45° 25′ 12″ — spaces between degree/minute/second groups + let expr = r#" + 45°  + 25′  + 12″ + "#; + test_braille("French", expr, "⠹⠱⠐⠕⠀⠣⠱⠄⠀⠡⠣⠄⠄")?; + return Ok(()); +} + +#[test] +fn units_0_degC_16_3_08() -> Result<()> { + let expr = r#"0 ℃"#; + test_braille("French", expr, "⠼⠀⠐⠕⠨⠉")?; + return Ok(()); +} + +#[test] +fn units_50_km_h_16_3_09() -> Result<()> { + let expr = r#"50 km/h"#; + test_braille("French", expr, "⠱⠼⠀⠅⠍⠌⠓")?; + return Ok(()); +} + +#[test] +fn units_3_4_m_s_16_3_10() -> Result<()> { + // 3,4 m·s⁻¹ + let expr = r#" + 3,4  + m×s-1 + "#; + test_braille("French", expr, "⠩⠂⠹⠀⠍⠔⠎⠈⠤⠡")?; + return Ok(()); +} + +#[test] +fn units_2h35min15s_16_3_11() -> Result<()> { + let expr = r#" + 2 h  + 35 min  + 15 s + "#; + test_braille("French", expr, "⠣⠀⠓⠀⠩⠱⠀⠍⠊⠝⠀⠡⠱⠀⠎")?; + return Ok(()); +} + +#[test] +fn units_7_L_16_3_12() -> Result<()> { + let expr = r#"7 L"#; + test_braille("French", expr, "⠻⠀⠨⠇")?; + return Ok(()); +} + +#[test] +fn units_50_cL_16_3_13() -> Result<()> { + let expr = r#"50 cL"#; + test_braille("French", expr, "⠱⠼⠀⠉⠨⠇")?; + return Ok(()); +} + +#[test] +fn units_4_MHz_16_3_14() -> Result<()> { + let expr = r#"4 MHz"#; + test_braille("French", expr, "⠹⠀⠨⠍⠨⠓⠵")?; + return Ok(()); +} + +#[test] +fn units_6_ohm_16_3_15() -> Result<()> { + let expr = r#"6 Ω"#; + test_braille("French", expr, "⠫⠀⠨⠘⠺")?; + return Ok(()); +} + +#[test] +fn units_5_uV_16_3_16() -> Result<()> { + let expr = r#"5 μV"#; + test_braille("French", expr, "⠱⠀⠘⠍⠨⠧")?; + return Ok(()); +} + +#[test] +fn units_9_angstrom_16_3_17() -> Result<()> { + let expr = r#"9 Å"#; + test_braille("French", expr, "⠪⠀⠨⠁⠈⠕")?; + return Ok(()); +} + #[test] fn power_ordinal_a_16_4_07() -> Result<()> { let expr= r#"(p+q)ième"#; diff --git a/tests/braille/French/other.rs b/tests/braille/French/other.rs new file mode 100644 index 000000000..bcead397b --- /dev/null +++ b/tests/braille/French/other.rs @@ -0,0 +1,367 @@ +// French braille tests from additional sources (generated candidates). +// Base reference: "NOTATION MATHÉMATIQUE BRAILLE" (Première édition janvier 2007) +// https://www.avh.asso.fr/sites/default/files/notation_mathematique_braille2_0.pdf +// 4e, 5e, etc., refer to grade levels. Numbers refer to relevant sections in the spec. + +use crate::common::*; +use anyhow::Result; + +// --- lot1: digits, letters, sets --- + +#[test] +fn digit_isolated_5_1_01() -> Result<()> { + let expr= r#"5"#; + test_braille("French", expr, "⠱")?; + return Ok(()); +} + +#[test] +fn digit_multidigit_312_1_02() -> Result<()> { + let expr= r#"312"#; + test_braille("French", expr, "⠩⠡⠣")?; + return Ok(()); +} + +#[test] +fn digit_decimal_3_14_1_03() -> Result<()> { + let expr= r#"3,14"#; + test_braille("French", expr, "⠩⠂⠡⠹")?; + return Ok(()); +} + +#[test] +fn digit_negative_minus_7_1_04() -> Result<()> { + let expr= r#"−7"#; + test_braille("French", expr, "⠤⠻")?; + return Ok(()); +} + +#[test] +fn infinity_plus_1_05() -> Result<()> { + let expr= r#"+∞"#; + test_braille("French", expr, "⠖⠘⠉")?; + return Ok(()); +} + +#[test] +fn partial_derivative_round_d_2_1_01() -> Result<()> { + let expr= r#"∂"#; + test_braille("French", expr, "⠐⠙")?; + return Ok(()); +} + +#[test] +fn set_naturals_blackboard_n_2_2_01() -> Result<()> { + let expr= r#"ℕ"#; + test_braille("French", expr, "⠨⠨⠝")?; + return Ok(()); +} + +#[test] +fn greek_alpha_lower_2_3_01() -> Result<()> { + let expr= r#"α"#; + test_braille("French", expr, "⠘⠁")?; + return Ok(()); +} + +#[test] +fn greek_delta_upper_2_3_02() -> Result<()> { + let expr= r#"Δ"#; + test_braille("French", expr, "⠨⠘⠙")?; + return Ok(()); +} + +#[test] +fn hebrew_aleph_2_4_01() -> Result<()> { + let expr= r#"ℵ"#; + test_braille("French", expr, "⠘⠘⠁")?; + return Ok(()); +} + +// --- lot2: operations, equality, parentheses --- + +#[test] +fn addition_simple_3_01() -> Result<()> { + let expr= r#"2+3"#; + test_braille("French", expr, "⠣⠖⠩")?; + return Ok(()); +} + +#[test] +fn multiplication_cross_3_02() -> Result<()> { + let expr= r#"2×3"#; + test_braille("French", expr, "⠣⠔⠩")?; + return Ok(()); +} + +#[test] +fn plus_or_minus_3_03() -> Result<()> { + let expr= r#"±5"#; + test_braille("French", expr, "⠖⠤⠱")?; + return Ok(()); +} + +#[test] +fn function_composition_3_04() -> Result<()> { + let expr= r#"f∘g"#; + test_braille("French", expr, "⠋⠸⠼⠛")?; + return Ok(()); +} + +#[test] +fn equality_arithmetic_4_01() -> Result<()> { + let expr= r#"2+3=5"#; + test_braille("French", expr, "⠣⠖⠩⠶⠱")?; + return Ok(()); +} + +#[test] +fn inequality_less_than_4_02() -> Result<()> { + let expr= r#"x<5"#; + test_braille("French", expr, "⠭⠐⠣⠱")?; + return Ok(()); +} + +#[test] +fn approximately_equal_pi_4_03() -> Result<()> { + let expr= r#"π≈3,14"#; + test_braille("French", expr, "⠘⠏⠐⠶⠩⠂⠡⠹")?; + return Ok(()); +} + +#[test] +fn parenthesis_simple_5_01() -> Result<()> { + let expr= r#"(a+b)"#; + test_braille("French", expr, "⠦⠁⠖⠃⠴")?; + return Ok(()); +} + +#[test] +fn parenthesis_nested_imbrication_5_02() -> Result<()> { + let expr= r#"f(g(x))"#; + test_braille("French", expr, "⠋⠐⠦⠛⠦⠭⠴⠐⠴")?; + return Ok(()); +} + +#[test] +fn braces_set_with_condition_5_03() -> Result<()> { + let expr= r#"{x:x>0}"#; + test_braille("French", expr, "⠨⠦⠭⠒⠭⠐⠜⠼⠨⠴")?; + return Ok(()); +} + +// --- 4e --- + +#[test] +fn linear_equation_4e_3_4_01() -> Result<()> { + let expr= r#"2x+3=7"#; + test_braille("French", expr, "⠣⠭⠖⠩⠶⠻")?; + return Ok(()); +} + +#[test] +fn inequality_4e_4_01() -> Result<()> { + let expr= r#"2x−1<5"#; + test_braille("French", expr, "⠣⠭⠤⠡⠐⠣⠱")?; + return Ok(()); +} + +#[test] +fn pythagoras_4e_9_3_01() -> Result<()> { + let expr= r#"a2+b2=c2"#; + test_braille("French", expr, "⠁⠈⠣⠖⠃⠈⠣⠶⠉⠈⠣")?; + return Ok(()); +} + +#[test] +fn square_root_simple_4e_10_01() -> Result<()> { + let expr= r#"25=5"#; + test_braille("French", expr, "⠜⠣⠱⠶⠱")?; + return Ok(()); +} + +#[test] +fn scientific_notation_4e_9_3_02() -> Result<()> { + let expr= r#"3×103"#; + test_braille("French", expr, "⠩⠔⠡⠼⠈⠩")?; + return Ok(()); +} + +#[test] +fn negative_exponent_4e_9_3_03() -> Result<()> { + let expr= r#"10−2=0,01"#; + test_braille("French", expr, "⠡⠼⠈⠤⠣⠶⠼⠂⠼⠡")?; + return Ok(()); +} + +#[test] +fn sin_function_full_4e_14_6_01() -> Result<()> { + let expr= r#"sin(30°)=12"#; + test_braille("French", expr, "⠎⠊⠝⠦⠩⠼⠐⠕⠴⠶⠡⠌⠣")?; + return Ok(()); +} + +#[test] +fn binomial_square_4e_9_3_5_01() -> Result<()> { + let expr= r#"(a+b)2=a2+2ab+b2"#; + test_braille("French", expr, "⠦⠁⠖⠃⠴⠈⠣⠶⠁⠈⠣⠖⠣⠁⠃⠖⠃⠈⠣")?; + return Ok(()); +} + +#[test] +fn square_root_complex_4e_10_8_01() -> Result<()> { + let expr= r#"a2+b2"#; + test_braille("French", expr, "⠜⠰⠁⠈⠣⠖⠃⠈⠣⠆")?; + return Ok(()); +} + +#[test] +fn probability_4e_5_7_01() -> Result<()> { + let expr= r#"P(A)=14"#; + test_braille("French", expr, "⠨⠏⠦⠨⠁⠴⠶⠡⠌⠹")?; + return Ok(()); +} + +// --- 5e --- + +#[test] +fn fraction_add_simple_5e_7_3_01() -> Result<()> { + let expr= r#"13+14=712"#; + test_braille("French", expr, "⠡⠌⠩⠖⠡⠌⠹⠶⠻⠌⠡⠣")?; + return Ok(()); +} + +#[test] +fn negative_add_5e_3_01() -> Result<()> { + let expr= r#"−5+3=−2"#; + test_braille("French", expr, "⠤⠱⠖⠩⠶⠤⠣")?; + return Ok(()); +} + +#[test] +fn literal_expression_5e_3_01() -> Result<()> { + let expr= r#"3a+2b"#; + test_braille("French", expr, "⠩⠁⠖⠣⠃")?; + return Ok(()); +} + +#[test] +fn percentage_5e_16_4_01() -> Result<()> { + let expr= r#"15%×60=9"#; + test_braille("French", expr, "⠡⠱⠐⠬⠔⠫⠼⠶⠪")?; + return Ok(()); +} + +#[test] +fn power_two_digit_base_5e_9_3_01() -> Result<()> { + let expr= r#"102=100"#; + test_braille("French", expr, "⠡⠼⠈⠣⠶⠡⠼⠼")?; + return Ok(()); +} + +#[test] +fn volume_cube_formula_5e_9_3_02() -> Result<()> { + let expr= r#"V=a3"#; + test_braille("French", expr, "⠨⠧⠶⠁⠈⠩")?; + return Ok(()); +} + +#[test] +fn fraction_complex_numerator_5e_7_8_01() -> Result<()> { + let expr= r#"a+b2"#; + test_braille("French", expr, "⠰⠁⠖⠃⠆⠌⠣")?; + return Ok(()); +} + +#[test] +fn triangle_area_5e_7_8_02() -> Result<()> { + let expr= r#"A=b×h2"#; + test_braille("French", expr, "⠨⠁⠶⠰⠃⠔⠓⠆⠌⠣")?; + return Ok(()); +} + +#[test] +fn angles_sum_greek_5e_2_3_16_01() -> Result<()> { + let expr= r#"α+β=90°"#; + test_braille("French", expr, "⠘⠁⠖⠘⠃⠶⠪⠼⠐⠕")?; + return Ok(()); +} + +#[test] +fn proportionality_ratio_5e_7_4_01() -> Result<()> { + let expr= r#"ab=cd"#; + test_braille("French", expr, "⠁⠌⠃⠶⠉⠌⠙")?; + return Ok(()); +} + +// --- 6e --- + +#[test] +fn addition_6e_3_01() -> Result<()> { + let expr= r#"12+7=19"#; + test_braille("French", expr, "⠡⠣⠖⠻⠶⠡⠪")?; + return Ok(()); +} + +#[test] +fn multiplication_6e_3_02() -> Result<()> { + let expr= r#"6×8=48"#; + test_braille("French", expr, "⠫⠔⠳⠶⠹⠳")?; + return Ok(()); +} + +#[test] +fn division_6e_3_03() -> Result<()> { + let expr= r#"15÷3=5"#; + test_braille("French", expr, "⠡⠱⠒⠩⠶⠱")?; + return Ok(()); +} + +#[test] +fn priority_no_parens_6e_3_4_01() -> Result<()> { + let expr= r#"3+4×2=11"#; + test_braille("French", expr, "⠩⠖⠹⠔⠣⠶⠡⠡")?; + return Ok(()); +} + +#[test] +fn parentheses_priority_6e_5_01() -> Result<()> { + let expr= r#"(3+4)×2=14"#; + test_braille("French", expr, "⠦⠩⠖⠹⠴⠔⠣⠶⠡⠹")?; + return Ok(()); +} + +#[test] +fn fraction_simple_6e_7_01() -> Result<()> { + let expr= r#"34"#; + test_braille("French", expr, "⠩⠌⠹")?; + return Ok(()); +} + +#[test] +fn fraction_equality_6e_7_4_01() -> Result<()> { + let expr= r#"12=24"#; + test_braille("French", expr, "⠡⠌⠣⠶⠣⠌⠹")?; + return Ok(()); +} + +#[test] +fn fraction_decimal_6e_7_1_01() -> Result<()> { + let expr= r#"34=0,75"#; + test_braille("French", expr, "⠩⠌⠹⠶⠼⠂⠻⠱")?; + return Ok(()); +} + +#[test] +fn square_exponent_6e_9_3_01() -> Result<()> { + let expr= r#"52=25"#; + test_braille("French", expr, "⠱⠈⠣⠶⠣⠱")?; + return Ok(()); +} + +#[test] +fn perpendicular_lines_6e_12_3_01() -> Result<()> { + let expr= r#"(AB)⊥(CD)"#; + test_braille("French", expr, "⠦⠨⠁⠨⠃⠴⠘⠳⠦⠨⠉⠨⠙⠴")?; + return Ok(()); +} From d743060a0c3818030b3a0ddd079742d942ef1503 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 6 Aug 2026 22:30:00 -0700 Subject: [PATCH 019/120] Add "(eau)" as a state (for French) Added several more chem arrows. --- src/chemistry.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/chemistry.rs b/src/chemistry.rs index 4b991dcef..461e50187 100644 --- a/src/chemistry.rs +++ b/src/chemistry.rs @@ -81,10 +81,15 @@ static MERGED_TOKEN: &str = "data-merged"; /// these can be in the base of an under/over script fn is_chem_equation_arrow(ch: char) -> bool { matches!(ch, - '→' | '➔' | '←' | '⟶' | '⟵' | '⤻' | '⇋' | '⇌' | + // reaction arrows + '→' | '➔' | '←' | '⟶' | '⟵' | '⤻' | '⇒' | '⇐' | '⟹' | '⟸' | + // equilibrium + '⇋' | '⇌' | '⇄' | '⇆' | '⥂' | '⥄' | '⥃' | + // mesomerism / resonance (left-right) + '↔' | '⟷' | '⇔' | '⟺' | + // vertical / other chem arrows '↑' | '↓' | '↿' | '↾' | '⇃' | '⇂' | '⥮' | '⥯' | '⇷' | '⇸' | '⤉' | '⤈' | - '⥂' | '⥄' | '⥃' | - '\u{1f8d0}' | '\u{1f8d1}' | '\u{1f8d2}' | '\u{1f8d3}' | '\u{1f8d4}' | '\u{1f8d5}' // proposed Unicode equilibrium arrows + '\u{1f8d0}' | '\u{1f8d1}' | '\u{1f8d2}' | '\u{1f8d3}' | '\u{1f8d4}' | '\u{1f8d5}' // new Unicode equilibrium arrows ) } @@ -969,7 +974,7 @@ fn likely_chem_subscript(subscript: Element) -> i32 { return 0; // not really much chem info about an integer subscript } else if subscript_name == "mi" { let text = as_text(subscript); - if text == "s" || text == "l" || text == "g" || text == "aq" { + if text == "s" || text == "l" || text == "g" || text == "aq" || text == "eau" { subscript.set_attribute_value(CHEM_STATE, "true"); return 2; } @@ -1691,7 +1696,7 @@ fn is_equilibrium_constant(mut mathml: Element) -> bool { // All instances seem to be upper case that I've seen. static SMALL_UPPER_ROMAN_NUMERAL: LazyLock = LazyLock::new(|| Regex::new(r"^\s*^(IX|IV|V?I{0,3})\s*$").unwrap()); -/// look for "(s), "(l)", "(g)", "(aq)" (could also use [...]) +/// look for "(s)", "(l)", "(g)", "(aq)", "(eau)" (could also use [...]) /// this might be called before canonicalization, but in clean_chemistry_mrow, we made sure "( xxx )" is grouped properly pub fn likely_chem_state(mathml: Element) -> i32 { @@ -1701,7 +1706,7 @@ pub fn likely_chem_state(mathml: Element) -> i32 { let contents_name = name(contents); if contents_name == "mi" || contents_name == "mtext" { let text = as_text(contents); - if text == "s" || text == "l" ||text == "g" ||text == "aq" { + if text == "s" || text == "l" || text == "g" || text == "aq" || text == "eau" { return text.len() as i32 + 1; // hack to count chars -- works because all are ASCII }; } From 0d7a39a8e99df4fe6b3c5fb92208645e74646cf7 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 6 Aug 2026 22:31:59 -0700 Subject: [PATCH 020/120] add more arrows. French at the moment doesn't seem to distinguish between different arrows that go in the same direction. That seems wrong, but the spec is silent on the alternatives --- Rules/Braille/French/unicode-full.yaml | 11 ++--------- Rules/Braille/French/unicode.yaml | 13 ++++++------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/Rules/Braille/French/unicode-full.yaml b/Rules/Braille/French/unicode-full.yaml index e27f214a6..c0bc5275e 100644 --- a/Rules/Braille/French/unicode-full.yaml +++ b/Rules/Braille/French/unicode-full.yaml @@ -19,18 +19,11 @@ - "": [t: ""] # 0xfeff (BOM / zero-width no-break space) - - "⇌": [t: "⠸⠻"] # 0x21CC (Rightwards harpoon over leftwards harpoon (equilibrium)) -# - "⥂": [t: ""] # 0x2942 (Rightwards arrow above short leftwards arrow (equilibrium, trend to the right)) -# - "⥄": [t: ""] # 0x2944 (Short rightwards arrow above leftwards arrow (equilibrium, trend to the left)) + - "⇌⥂⥄⥃": [t: "⠸⠻"] # 0x21CC/2942/2944/2943 equilibrium (incl. trend variants) # Mappings for some new Unicode chemistry arrows/symbols # These repeat the short arrow version because that is what is in the spec (GTM 16), but the spec should really use the long version - - "🣑": [t: "⠸⠻"] # 0x1F8D1 LONG RIGHTWARDS HARPOON OVER LONG LEFTWARDS HARPOON (equilibrium) -# - "🣒": [t: ""] # 0x1f8d2 LONG RIGHTWARDS HARPOON ABOVE SHORT LEFTWARDS HARPOON(equilibrium, trend to the right) -# - "🣓": [t: ""] # 0x1f8d3 SHORT RIGHTWARDS HARPOON ABOVE LONG LEFTWARDS HARPOON (equilibrium, trend to the left)) - -# - "🣐": [t: ""] # 0x1F8D0 LONG RIGHTWARDS ARROW OVER LONG LEFTWARDS ARROW -# - "🣕": [t: ""] # 0x1F8D5 SHORT LEFTWARDS HARPOON ABOVELONG RIGHTWARDS HARPOON + - "🣐🣑🣒🣓🣔🣕": [t: "⠸⠻"] # 0x1F8D0–1F8D5 long equilibrium / related chem arrows # - "🣖": [t: ""] # 0x1F8D6 LONG RIGHTWARDS ARROW WITH THROUGH X # - "🣗": [t: ""] # 0x1F8D7 LONG RIGHTWARDS ARROW WITH DOUBLE SLASH # - "": [t: ""] # 0x1F8D8 LONG LEFT RIGHT ARROW WITH DEPENDENT LOBE (no clue what to do) diff --git a/Rules/Braille/French/unicode.yaml b/Rules/Braille/French/unicode.yaml index ce310bb3c..6da5cc310 100644 --- a/Rules/Braille/French/unicode.yaml +++ b/Rules/Braille/French/unicode.yaml @@ -251,7 +251,11 @@ if: "parent::m:mrow/*[self::m:mtable] or (following-sibling::*[1][self::m:mtable] and count(following-sibling::*[1]/*) = 1)" then: [t: "⠸⠦"] else: [t: "⠨⠦"] # regular brace - - "|": [t: "⠿"] # 0x007C (vertical bar) + - "|": # 0x007C (vertical bar) + - test: + if: "parent::m:mrow/*[self::m:mtable]" + then: [t: "⠸⠿"] # multi-line (linear matrix/determinant) + else: [t: "⠿"] # single-line - "}": # 0x007D (right curly brace)) - test: if: "parent::m:mrow/*[self::m:mtable]" @@ -285,12 +289,7 @@ if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" then: [t: "⠘⠱"] else: [t: "⠘⠻"] - - "→": # 0x2192 (right pointing arrow) - - test: - if: "parent::m:mover and not(following-sibling::*)" - then: [t: "⠨⠒"] - else: [t: "⠸⠱"] - - "⇀": # 0x2192 (right pointing arrow) + - "→⇀": # 0x2192 (right pointing arrow) - test: if: "parent::m:mover and not(following-sibling::*)" then: [t: "⠨⠒"] From 363fc51331bdcfe3104a3fc5ce11fdaa822621f7 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 6 Aug 2026 22:32:55 -0700 Subject: [PATCH 021/120] removed some intent attrs that were not needed --- tests/braille/French/chemistry.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/braille/French/chemistry.rs b/tests/braille/French/chemistry.rs index 97528e114..8ce715f5c 100644 --- a/tests/braille/French/chemistry.rs +++ b/tests/braille/French/chemistry.rs @@ -197,7 +197,7 @@ fn reaction_III_2_2() -> Result<()> { #[test] fn reaction_III_2_3() -> Result<()> { // HCl(g)+H₂O(l)→(eau) H₃O⁺(aq)+Cl⁻(aq) — line-continuation ⠐ omitted - let expr = r#" + let expr = r#" HCl(g) + H2O(l) @@ -327,7 +327,7 @@ fn redox_IV_1_3() -> Result<()> { #[test] fn mesomerism_IV_2_1() -> Result<()> { // (CH₃)₂C=O ↔ (CH₃)₂C⁺−O⁻ - let expr = r#" + let expr = r#" (CH3)2 C=O ↔ From 9588ed97a4182fc7511a602ecd41fc88bc2bbdf9 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 6 Aug 2026 22:33:33 -0700 Subject: [PATCH 022/120] uncomment a determinant test after fixing the braille --- tests/braille/French/french.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/braille/French/french.rs b/tests/braille/French/french.rs index 6b1accc72..4a823d0b6 100644 --- a/tests/braille/French/french.rs +++ b/tests/braille/French/french.rs @@ -467,15 +467,16 @@ fn restriction_of_f_to_13_2_04() -> Result<()> { return Ok(()); } -// #[test] -// fn determinant_tabular_13_3_01() -> Result<()> { -// let expr= r#"I=| -// 10 -// 23 -// |=3"#; -// test_braille("French", expr, "not correct⠨⠊⠶⠿⠡⠀⠼⠿ ⠿⠣⠀⠩⠿⠶⠩")?; -// return Ok(()); -// } +#[test] +fn determinant_tabular_13_3_01() -> Result<()> { + let expr= r#"I=| + 10 + 23 + |=3"#; + test_braille("French", expr, "⠨⠊⠶⠸⠿⠡⠀⠼⠀⠠⠜⠀⠣⠀⠩⠸⠿⠶⠩")?; + return Ok(()); +} + #[test] fn matrix_linear_13_3_02() -> Result<()> { let expr= r#"J=( From 4408cb20178bfb7c6e75e13f1ddb39b19813401a Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 6 Aug 2026 23:08:37 -0700 Subject: [PATCH 023/120] Add rules for multiple lines in a script position done via an mtable or a more restricted version for nested munder/mover. They add a "," between lines. This was done for limit_x_tends_1_14_2_01_corrected and limit_x_tends_1_14_2_02_corrected --- Rules/Braille/French/French_Rules.yaml | 46 +++++++++++++++++++++----- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/Rules/Braille/French/French_Rules.yaml b/Rules/Braille/French/French_Rules.yaml index 66a14b325..3c77813a5 100644 --- a/Rules/Braille/French/French_Rules.yaml +++ b/Rules/Braille/French/French_Rules.yaml @@ -125,22 +125,24 @@ name: default tag: [mtr, mlabeledtr] match: "." + # When an mtable is used in a script position (e.g. stacked lim conditions), §14.2 + # joins rows with a comma (⠂) rather than the matrix row-change (⠠⠜). + variables: + - InScriptPosition: "parent::m:mtable[preceding-sibling::* and IsNode(.., 'modified')]" replace: - test: if: "preceding-sibling::*" - then: [t: "W⠠⠜W"] - - # - test: - # if: .[self::m:mlabeledtr] - # then: - # - t: "⠗⠕⠺W⠇⠁⠃⠑⠇⠸⠒" # "row label:" - # - x: "*[1]/*" # contents of row label + then: + - test: + if: "$InScriptPosition" + then: [t: "⠂"] # comma between script-position rows + else: [t: "W⠠⠜W"] # matrix/system row change - test: if: .[self::m:mlabeledtr] then: [x: "*[position()>1]"] else: {x: "*"} - test: - if: "following-sibling::* " + if: "following-sibling::* and not($InScriptPosition)" then: [t: "W"] - @@ -427,6 +429,34 @@ - x: "*[2]" - x: "*[1]" +- + # Nested munder/mover for lim / large operators (§14.2): join the two script + # conditions with a comma (⠂), like script-position mtables — one script + # indicator and one block around both conditions. + name: nested-lim-or-largeop-conditions + tag: [munder, mover] + match: "((self::m:munder and *[1][self::m:munder]) or (self::m:mover and *[1][self::m:mover])) and + *[1]/*[1][IsInDefinition(., 'Braille', 'LargeOperators') or .='lim' or .='Lim']" + replace: + - t: "b" + - test: + if: "NeedsToBeGrouped(*[1]/*[1], 'French', true())" + then: + - t: "o" + - x: "*[1]/*[1]" + - t: "c" + else: [x: "*[1]/*[1]"] + - test: + - if: "self::m:munder" + then: [t: "⠢⠢"] + else: [t: "⠈⠈"] + - t: "o" + - x: "*[1]/*[2]" + - t: "⠂" + - x: "*[2]" + - t: "c" + - t: "e" + - name: default tag: [msub, msup, munder, mover] From 5c482681655dbd37a1df66869a983cbaff4e0d2d Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 6 Aug 2026 23:10:56 -0700 Subject: [PATCH 024/120] a linebreaks to MathML to make it easier to read --- tests/braille/French/french.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/braille/French/french.rs b/tests/braille/French/french.rs index 4a823d0b6..1f3348c1a 100644 --- a/tests/braille/French/french.rs +++ b/tests/braille/French/french.rs @@ -553,7 +553,11 @@ fn limit_x_tends_and_x_lt_1_14_2_02_corrected_1() -> Result<()> { #[test] fn limit_x_tends_and_x_lt_2_14_2_02_corrected_2() -> Result<()> { - let expr= r#"limx→4x>4f(x)=−∞"#; + let expr= r#" + limx→4 + x>4 + + f(x)=−∞"#; test_braille("French", expr, "⠇⠊⠍⠢⠢⠰⠭⠸⠱⠹⠂⠭⠐⠜⠹⠆⠋⠦⠭⠴⠶⠤⠘⠉")?; return Ok(()); } From e5a9c87d81d817ce3ecb31f9bb62c7c755a0151f Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 6 Aug 2026 23:27:55 -0700 Subject: [PATCH 025/120] add more arrow chars -- I thought I did this in a previous commit, but apparently not. --- Rules/Braille/French/unicode.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Rules/Braille/French/unicode.yaml b/Rules/Braille/French/unicode.yaml index 6da5cc310..89f876bd6 100644 --- a/Rules/Braille/French/unicode.yaml +++ b/Rules/Braille/French/unicode.yaml @@ -283,18 +283,18 @@ - "€": [t: "⠘⠑"] # 0x20AC (euro) - "Å": [t: "⠨⠁⠈⠕"] # 0x212B (angstrom) - "ℵ": [t: "HL⠁"] # 0x2135 (alef) - - "←": [t: "⠸⠪"] # 0x2190 (left pointing arrow) - - "↑": # 0x2191 (arrow pointing up) + - "←⟵": [t: "⠸⠪"] # 0x2190/27F5 (left / long left arrow) + - "↑↿↾⤉": # 0x2191/21BF/21BE/2909 (up arrow / harpoons / barred) - test: if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" then: [t: "⠘⠱"] else: [t: "⠘⠻"] - - "→⇀": # 0x2192 (right pointing arrow) + - "→⇀➔⟶⤻": # 0x2192/21C0/2794/27F6/293B (right arrow / harpoon / long / curved) - test: if: "parent::m:mover and not(following-sibling::*)" then: [t: "⠨⠒"] else: [t: "⠸⠱"] - - "↓": # 0x2193 (arrow pointing down) + - "↓⇃⇂⤈": # 0x2193/21C3/21C2/2908 (down arrow / harpoons / barred) - test: if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" then: [t: "⠨⠱"] @@ -304,16 +304,16 @@ - "↗": [t: "⠘⠱"] # 0x2197 (north east arrow) - "↘": [t: "⠨⠱"] # 0x2198 (south east arrow) - "↙": [t: "⠨⠪"] # 0x2199 (south west arrow) - - "↚": [t: "⠨⠸⠪"] # 0x219A (leftwards arrow with stroke) - - "↛": [t: "⠨⠸⠱"] # 0x219B (rightwards arrow with stroke) + - "↚⇷": [t: "⠨⠸⠪"] # 0x219A/21F7 (leftwards arrow with stroke) + - "↛⇸": [t: "⠨⠸⠱"] # 0x219B/21F8 (rightwards arrow with stroke) - "↦": [t: "⠐⠱"] # 0x21A6 (rightwards arrow from bar (maps to)) - - "⇄": [t: "⠸⠻"] # 0x21C4 (arrow pointing right over arrow pointing left) + - "⇄⟷⇌⇋⇆⥮⥯": [t: "⠸⠻"] # 0x21C4/27F7/21CC/21CB/21C6/296E/296F (equilibrium / reversibility) - "⇍": [t: "⠨⠐⠒"] # 0x21CD (leftwards double arrow with stroke) - "⇎": [t: "⠨⠐⠒⠂"] # 0x21CE (left right double arrow with stroke (not equivalent)) - "⇏": [t: "⠨⠒⠂"] # 0x21CF (rightwards double arrow with stroke (does not imply)) - - "⇐": [t: "⠐⠒"] # 0x21D0 (leftwards double arrow) - - "⇒": [t: "⠒⠂"] # 0x21D2 (rightwards double arrow (implication)) - - "⇔": [t: "⠐⠒⠂"] # 0x21D4 (left right double arrow (double implication, equivalence)) + - "⇐⟸": [t: "⠐⠒"] # 0x21D0/27F8 (leftwards double / long) + - "⇒⟹": [t: "⠒⠂"] # 0x21D2/27F9 (rightwards double / long) + - "⇔⟺": [t: "⠐⠒⠂"] # 0x21D4/27FA (left right double / long) - "∀": [t: "⠸⠌"] # 0x2200 (universal quantifier (for all, for each, for every)) - "∁": [t: "⠨⠩"] # 0x2201 (capital english c (complement)) - "∂": [t: "⠐⠙"] # 0x2202 (partial derivative (round d)) From c2dbcc64c3e72fd7973a0f369f8868754211a550 Mon Sep 17 00:00:00 2001 From: mgros Date: Sat, 15 Aug 2026 03:14:51 +0200 Subject: [PATCH 026/120] Add MathJax demo link to developer docs for rendering inspection --- docs/developers.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/developers.md b/docs/developers.md index 2bc2f71e2..04440907c 100644 --- a/docs/developers.md +++ b/docs/developers.md @@ -74,6 +74,10 @@ fn test_simple_fraction() { } ``` +## Helpful Links + +* [MathJax Demo](https://www.mathjax.org/#demo) (archived) lets you paste a MathML fragment and inspect its browser rendering. It can be useful when checking whether an input expression itself renders as expected. + ### Test Coverage Test coverage helps identify which parts of the code are exercised by tests and which parts need more testing. @@ -121,4 +125,4 @@ open target/coverage/html/index.html **Alternative: IDE Integration** -Many Rust IDEs provide built-in test coverage support, like RustRover or VSCode. \ No newline at end of file +Many Rust IDEs provide built-in test coverage support, like RustRover or VSCode. From eebb4be6e7ef538f3f55c3b30985d9b5c128d6d7 Mon Sep 17 00:00:00 2001 From: mgros Date: Sat, 15 Aug 2026 03:16:11 +0200 Subject: [PATCH 027/120] . --- docs/developers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developers.md b/docs/developers.md index 04440907c..331a16d78 100644 --- a/docs/developers.md +++ b/docs/developers.md @@ -76,7 +76,7 @@ fn test_simple_fraction() { ## Helpful Links -* [MathJax Demo](https://www.mathjax.org/#demo) (archived) lets you paste a MathML fragment and inspect its browser rendering. It can be useful when checking whether an input expression itself renders as expected. +* [MathJax Demo](https://www.mathjax.org/#demo): lets you paste a MathML fragment and inspect its browser rendering. ### Test Coverage From 8e886cc25bfb4952aa7532f5c23c6244d6928a8f Mon Sep 17 00:00:00 2001 From: NSoiffer Date: Fri, 14 Aug 2026 23:55:14 -0700 Subject: [PATCH 028/120] Merge changes (mostly unicode) from hu branch (#688) * Add few tweaks into the Rules/Languages/hu/definitions.yaml file, Added missing definitions.rs test file the test/Languages/hu directory, and added SimpleSpeak test directory the mroot and menclose tests (#646) * Do more localization in Rules/Languages/hu/definitions.yaml Signed-off-by: Attila Hammer * Add in Simplespeak tests the definition, mroot and menclose tests Signed-off-by: Attila Hammer * fix some werbs in intervals into rules/Languages/hu/ClearSpeak_rules.yaml and SimpleSpeak_rules.yaml Signed-off-by: Attila Hammer * Fix tests after interval Clearspeak and SimpleSpeak verb change Signed-off-by: Attila Hammer --------- Signed-off-by: Attila Hammer * Apply Hungarian unicode fixes from PR #673 --------- Signed-off-by: Attila Hammer Co-authored-by: hammera --- Rules/Languages/hu/ClearSpeak_Rules.yaml | 4 +- Rules/Languages/hu/SimpleSpeak_Rules.yaml | 4 +- Rules/Languages/hu/definitions.yaml | 99 +- Rules/Languages/hu/unicode-full.yaml | 21 +- Rules/Languages/hu/unicode.yaml | 5 - tests/Languages/hu.rs | 5 +- tests/Languages/hu/ClearSpeak/functions.rs | 16 +- tests/Languages/hu/SimpleSpeak/functions.rs | 12 +- tests/Languages/hu/SimpleSpeak/menclose.rs | 266 +++++ tests/Languages/hu/SimpleSpeak/mroot.rs | 178 ++++ tests/Languages/hu/definitions.rs | 1026 +++++++++++++++++++ tests/Languages/hu/shared.rs | 2 +- 12 files changed, 1552 insertions(+), 86 deletions(-) create mode 100644 tests/Languages/hu/SimpleSpeak/menclose.rs create mode 100644 tests/Languages/hu/SimpleSpeak/mroot.rs create mode 100644 tests/Languages/hu/definitions.rs diff --git a/Rules/Languages/hu/ClearSpeak_Rules.yaml b/Rules/Languages/hu/ClearSpeak_Rules.yaml index 02040fc5d..73d8eaf5e 100644 --- a/Rules/Languages/hu/ClearSpeak_Rules.yaml +++ b/Rules/Languages/hu/ClearSpeak_Rules.yaml @@ -623,7 +623,7 @@ - test: if: "starts-with(name(.), 'open')" then: [T: "nem"] # phrase(the interval from a to b 'not' including b) - - T: "tartalmazza a" # phrase(the interval from a to b not 'including' b) + - T: "tartalmazza a(z)" # phrase(the interval from a to b not 'including' b) - x: "*[1]" - T: "elemet" # logic to deal with [not] arg #1 @@ -647,7 +647,7 @@ if: "name(.) = 'open-interval' or name(.) = 'closed-open-interval'" then: [T: "nem"] # phrase(the interval 'not' including a) - T: "tartalmazza" # phrase(the interval not 'including' a) - - T: "a" + - T: "a(z)" - x: "*[2]" - T: "elemet" # onto the [not] [including]... part diff --git a/Rules/Languages/hu/SimpleSpeak_Rules.yaml b/Rules/Languages/hu/SimpleSpeak_Rules.yaml index b5f263c3a..5037a4122 100644 --- a/Rules/Languages/hu/SimpleSpeak_Rules.yaml +++ b/Rules/Languages/hu/SimpleSpeak_Rules.yaml @@ -574,7 +574,7 @@ - test: if: "starts-with(name(.), 'open')" then: [T: "nem"] # phrase(the interval from a to b 'not' including b) - - T: "tartalmazza a" # phrase(the interval from a to b not 'including' b) + - T: "tartalmazza a(z)" # phrase(the interval from a to b not 'including' b) - x: "*[1]" - T: "elemet" # logic to deal with [not] arg #1 @@ -598,7 +598,7 @@ if: "name(.) = 'open-interval' or name(.) = 'closed-open-interval'" then: [T: "nem"] # phrase(the interval 'not' including a) - T: "tartalmazza" # phrase(the interval not 'including' a) - - T: "a" + - T: "a(z)" - x: "*[2]" - T: "elemet" # onto the [not] [including]... part diff --git a/Rules/Languages/hu/definitions.yaml b/Rules/Languages/hu/definitions.yaml index c0e75bee2..bdc649424 100644 --- a/Rules/Languages/hu/definitions.yaml +++ b/Rules/Languages/hu/definitions.yaml @@ -23,28 +23,28 @@ #"closed-open-interval":"function=interval between; included and", #"open-closed-interval":"function=interval between; and included", #"open-interval":"function=open interval between; and", - "inverse":"function=inverse || postfix=inverse", + "inverse":"function=inverz || postfix=inverz", "domain": "function= ; domén", "codomain": "function= ; kodomán", - "image":"function=image", + "image":"function=kép", #"fraction":"function=fraction; over; end fraction", # NOTE: Fails - "mixed-fraction":"infix=and", # NOTE: in website says function, but follow infix speech pattern. - "greatest-common-divisor": "function=gcd: a GCD: a legnagyobb közös osztó", - "quotient":"function=szám rész; osztva ezzel:", # NOTE: Logic somewhere here failing, becomes "divided by of x comma, y" instead of "integer part of x divided by y" - "evaluated-at":"infix=értékelték", - "remainder":"function=a maradék; osztva ezzel:", + "mixed-fraction":"infix=és", # NOTE: in website says function, but follow infix speech pattern. + "greatest-common-divisor": "function=a legnagyobb közös osztó", + "quotient":"function=egész rész osztva", # NOTE: Logic somewhere here failing, becomes "divided by of x comma, y" instead of "integer part of x divided by y" + "evaluated-at":"infix=értékelve || postfix=értékként", + "remainder":"function=a maradék, osztva", "max":"function=max", "min":"function=min", "power":"infix=a hatványra", "root":"function=root", - "imaginary-part": "function=képzeletbeli rész: a képzeletbeli rész: a képzeletbeli rész", + "imaginary-part": "a képzeletbeli rész", "least-common-multiple": "function=lcm: a legkisebb közös többszörös: a legkisebb közös többszörös", "complex-conjugate":"function=Komplex konjugált", "complex-arg":"function=argumentum", - "polar-coordinate":"function=poláris koordináta; vessző", - "spherical-coordinate":"function=gömb koordináta; vessző; vessző", - "cartesian-coordinate":"function=derékszögű koordináta; vessző", - "coordinate":"function=koordináta; vessző", + "polar-coordinate":"function=poláris koordináta", + "spherical-coordinate":"function=gömb koordináta", + "cartesian-coordinate":"function=derékszögű koordináta", + "coordinate":"function=koordináta", "floor":"function=lépcső", "ceiling":"function=mennyezet", "round":"function=kerekített érték", @@ -60,7 +60,7 @@ "dimension": "function=; dimenzió : dimenzió: a dimenzió; dimenzió vége", "homomorphism": "function= ; homomorfizmus : homomorfizmus: a homomorfizmus; homomorfizmus vége", "kernel": "function= ; kernel : kernel: a kernel; kernel vége", - "dot-product": "infix=skaláris szorzat: skaláris szorzat: skaláris szorzat: skaláris szorzat", + "dot-product": "infix=skaláris szorzat", #"divergence": "function= ; dihv: divergencia: divergencia; divergencia vége", #"laplacian": "function=laplaciáns", # speech engines don't do a good job with "laplacian" @@ -69,19 +69,20 @@ ### Calculus "definite-integral":"function=integrál felett || function=integrál innen:; ide:", # Property ??? "derivative":"function=a deriváltja; mely erre vonatkozik:", # Property ??? - "tends-to":"infix=általában", + "tends-to":"infix=ehhez tart", "tends-to-from-above":"infix=általában alulról indul", - + "tends-to-from-below":"infix=alulról indul", ### Sets "set": "function= ; halmaz: a halmaz", + "set-difference":"function=különbséghalmaz || infix=mínusz || postfix=különbséghalmaza", # NOTE: not tested "complement":"function=komplemens", - "empty-set":"nofix=üres halmaz", + "empty-set":"function=üres halmaz", "cardinality":"function=számosság", # NOTE: does not have a defined speech template in website "list":"function=lista", "tuple": "function= ; tuple: a tuple", ### Sequence and Series "sum":"function= ; összegzés : összeg ; ", #: sum over : sum from; to", - "product":"function=produktum || function=produktum vége || function=produktum innen:; ide:", + "product":"function=szorzat || function=szorzat vége || function=szorzat innen ide", ### Elementary classical functions "sine":"function=szinusz: szinusz", @@ -91,23 +92,23 @@ "cosecant":"function=koszekáns: koszekáns", "cotangent":"function=kotangens: kotangens", "arcsine":"function=arkusz szinusz", - #"arccosine":"function=arkusz koszinusz", + "arccosine":"function=arkusz koszinusz", "arctangent":"function=arkusz tangens", "arcsecant":"function=arkusz szekáns", "arccosecant":"function=arkusz koszekáns", "arccotangent":"function=arkusz kotangens", "hyperbolic-sine":"function=hiperbolikus szinusz", "hyperbolic-cosine":"function=hiperbolikus koszinusz", - "hyperbolic-tangent":"function=hiperbolikus tangenstanch", - "hyperbolic-secant":"function=sech", - "hyperbolic-cosecant":"function=cosech", - "hyperbolic-cotangent":"function=coth", - "arc-hyperbolic-sine":"function=arc shine", - "arc-hyperbolic-cosine":"function=arc cosh", - "arc-hyperbolic-tangent":"function=arc tanch", - "arc-hyperbolic-secant":"function=arc sech", - "arc-hyperbolic-cosecant":"function=arc cosech", - "arc-hyperbolic-cotangent":"function=arc coth", + "hyperbolic-tangent":"function=hiperbolikus tangens", + "hyperbolic-secant":"function=hiperbolikus szekáns", + "hyperbolic-cosecant":"function=hiperbolikus koszekáns", + "hyperbolic-cotangent":"function=hiperbolikus kotangens", + "arc-hyperbolic-sine":"function=arkusz hiperbolikus szinusz", + "arc-hyperbolic-cosine":"function=arkusz hiperbolikus koszinusz", + "arc-hyperbolic-tangent":"function=arkusz hiperbolikus tangens", + "arc-hyperbolic-secant":"function=arkusz hiperbolikus szekáns", + "arc-hyperbolic-cosecant":"function=arkusz hiperbolikus koszekáns", + "arc-hyperbolic-cotangent":"function=arkusz hiperbolikus kotangens", "exponential":"function=exponenciális", "natural-logarithm": "function=l n: természetes alapú logaritmus: természetes alapú logaritmus", "logarithm":"function=logaritmus", ##Check arity 2 @@ -138,7 +139,7 @@ ### Constants and Sets "set-of-integers":"nofix=ℤ: az összes egész szám halmaza", - "set-of-reals":"nofix=ℝ: az összes valós szám halmaza", + "set-of-reals":"nofix=valós számok halmaza: az összes valós szám halmaza", "set-of-rationals":"nofix=ℚ: az összes racionális szám halmaza", "set-of-natural-numbers":"nofix=ℕ: az összes természetes szám halmaza", "set-of-complex-numbers":"nofix=ℂ: az összes komplex szám halmaza", @@ -167,13 +168,13 @@ "highlight":"postfix=kiemelt", "least-common-denominator":"function=legkisebb közös nevező", "rate":"infix=per", - "constraint":"infix= ; korlátozással; ", + "constraint":"infix= ; feltétellel; ", "binomial-coefficient":"infix=választ", "pochhammer":"function=permutáció", "permutation-cycle":"function=permutációs ciklus", - "embellished-name":"infix=jegyzetekkel", - + "embellished-name":"infix=megjegyzéssel", + "annotation":"infix= ; ami ;", # NOTE: Follows the same order as indexed-by, in site listed as function. Should it be infix? ### Other ## Default fixity function "curl": "function=rotáció", @@ -185,7 +186,7 @@ "angle": "prefix=szög", "angle-measure": "prefix=szögmérés", "change": "prefix=változás", - "for-all": "prefix=mindenkinek", + "for-all": "prefix=minden", "measured-angle": "prefix=mért szög", "not": "prefix=nem", "number-of": "prefix=száma", @@ -198,7 +199,7 @@ ## Default fixity infix "and": "infix=és", - "applied-to": "infix=alkalmazva erre", + "applied-to": "infix=alkalmazva", "approximately": "infix=hozzávetőlegesen", "congruent": "infix=egybeesik", "cartesian-product": "infix=derékszögű szorzat", @@ -210,7 +211,7 @@ "does-not-belong-to": "infix=nem tartozik hozzá", "does-not-divide": "infix=nem osztja", "downwards-diagonal-ellipsis": "infix=lefelé mutató átlós ellipszis", - "direct-product": "infix=direkt produktum", + "direct-product": "infix=direkt szorzat", "element-of": "infix=eleme", "ellipsis": "infix=ellipszis", "equals": "infix=egyenlő", @@ -222,7 +223,7 @@ "identically-equals": "infix=teljesen egyenlő", "if-and-only-if": "infix=akkor és csak akkor, ha", "implies": "infix=utal", - "inner-product": "infix=belső produktum", + "inner-product": "infix=belső szorzat", "intersection": "infix=keresztezi", "less-than": "infix=kisebb, mint", "less-than-or-equal-to": "infix=kisebb vagy egyenlő, mint", @@ -231,13 +232,13 @@ "member-of": "infix=tagja", "minus-or-plus": "infix=mínusz vagy plusz", "not-subset": "infix=nem részhalmaza", - "not-superset": "infix=nem szuperhalmaz", + "not-superset": "infix=nem szuperhalmaza", "not-equal-to": "infix=nem egyenlő", "not-member-of": "infix=nem tagja", "not-parallel-to": "infix=nem párhuzamos", - "obtained-from": "infix=származó", + "obtained-from": "infix=innen származik", "or": "infix=vagy", - "outer-product": "infix=külső produktum", + "outer-product": "infix=külső szorzata", "parallel-to": "infix=párhuzamos", "perpendicular": "infix=merőleges", "plus": "infix=plusz || prefix=pozitív", # Prefix not tested @@ -246,16 +247,16 @@ "precedes": "infix=megelőzi", "proportional": "infix=arányos", "range-separator": "infix=keresztül", - "ratio": "infix=hányados", + "ratio": "infix=arány", "similar": "infix=hasonló", "subset": "infix=részhalmaza", "subset-or-equal": "infix=részhalmaza vagy egyenlő", - "succeeds": "infix=sikerül", + "succeeds": "infix=követi", "such-that": "infix=úgy, hogy", "superset": "infix=szuperhalmaza", - "superset-or-equal": "infix=szuperhalmaz vagy egyenlő", + "superset-or-equal": "infix=szuperhalmaza vagy egyenlő", "tilde": "infix=tilde", - "times": "infix=szorzata", + "times": "infix=szorozva", "union": "infix=unió", "upwards-diagonal-ellipsis": "infix=felfelé átlós ellipszis", "vertical-ellipsis": "infix=függőleges ellipszis", @@ -270,9 +271,9 @@ "distance":"nofix=távolság; távolság: távolság", "probability":"nofix=P: valószínűség", "radius":"nofix=r: sugár", - "volume":"nofix=V: hangerő || function=", + "volume":"nofix=hangerő: hangerő || function=hangerő", "exponential-e":"nofix=e", - "imaginary-i":"nofix=i", + "imaginary-i":"nofix=képzeletbeli én", "differential-d":"nofix=d", "golden-ratio":"nofix=aranymetszés", @@ -290,11 +291,11 @@ # These are the parts of a formula that can be navigated to "large-op": "base; lower limit; upper limit", "mfrac": "numerator; denominator", - "fraction": "numerator; denominator", - "msqrt": "root", - "square-root": "root", + "fraction": "számláló; nevező", + "msqrt": "gyök", + "square-root": "gyök", "mroot": "root; root index", - "root": "root; root index", + "root": "gyök; gyök index", "msub": "base; subscript", "sub": "base; subscript", "logarithm-with-base": "base", diff --git a/Rules/Languages/hu/unicode-full.yaml b/Rules/Languages/hu/unicode-full.yaml index 2926422e0..4ab669c7a 100644 --- a/Rules/Languages/hu/unicode-full.yaml +++ b/Rules/Languages/hu/unicode-full.yaml @@ -273,7 +273,7 @@ - "‱": [T: "per tízezer"] # 0x2031 (en: 'per ten thousand', DeepL translation) - "′": [T: "prím"] # 0x2032 (en: 'prime', DeepL translation) - "″": [T: "dupla prím"] # 0x2033 (en: 'double prime', DeepL translation) - - "‴": [T: "hármas ikerprím"] # 0x2034 (en: 'triple prime', DeepL translation) + - "‴": [T: "hármas prím"] # 0x2034 (en: 'triple prime', DeepL translation) - "‵": [T: "fordított prím"] # 0x2035 (en: 'reversed prime', DeepL translation) - "‶": [T: "fordított dupla prím"] # 0x2036 (en: 'reversed double prime', DeepL translation) - "‷": [T: "fordított hármas prím"] # 0x2037 (en: 'reversed triple prime', DeepL translation) @@ -951,11 +951,6 @@ - "⊢": [T: "bizonyítja"] # 0x22a2 (en: 'proves', DeepL translation) - "⊣": [T: "nem enged"] # 0x22a3 (en: 'does not yield', DeepL translation) - "⊤": [T: "felső"] # 0x22a4 (en: 'top', DeepL translation) - - "⊥": # 0x22a5 - - test: - if: "$Verbosity!='Terse'" - then: [T: "a(z)"] # (en: 'is', DeepL translation) - - T: "alul" # (en: 'bottom', DeepL translation) - "⊦": [T: "redukálódik"] # 0x22a6 (en: 'reduces to', DeepL translation) - "⊧": [T: "modellezi"] # 0x22a7 (en: 'models', DeepL translation) - "⊨": # 0x22a8 @@ -1543,11 +1538,15 @@ - "➾": [T: "nyitott körvonalú jobbra mutató nyíl"] # 0x27be (en: 'open-outlined right arrow', DeepL translation) - "⟀": [T: "háromdimenziós szög"] # 0x27c0 (en: 'three dimensional angle', DeepL translation) - "⟁": [T: "fehér háromszög, benne egy kis fehér háromszög"] # 0x27c1 (en: 'white triangle containing small white triangle', DeepL translation) - - "⟂": # 0x27c2 - - test: - if: "$Verbosity!='Terse'" - then: [T: "a(z)"] # (en: 'is', DeepL translation) - - T: "merőleges" # (en: 'perpendicular to', DeepL translation) + - "⟂⊥": # 0x27c2, 22A5 (22A5 wrongly used for perpendicular by most editors) + - test: + - if: "$SpeechStyle = 'LiteralSpeak' and text()='⊥'" # 22A5 (up tack/bottom) + then: [T: "felfelé mutató merőleges"] + - else: + - test: + if: "$Verbosity!='Terse'" + then: [T: "a(z)"] + - T: "merőleges" - "⟃": # 0x27c3 - test: if: "$Verbosity!='Terse'" diff --git a/Rules/Languages/hu/unicode.yaml b/Rules/Languages/hu/unicode.yaml index 41f8263f2..16561b733 100644 --- a/Rules/Languages/hu/unicode.yaml +++ b/Rules/Languages/hu/unicode.yaml @@ -297,7 +297,6 @@ - "⁢": [T: ""] # 0x2062 - "⁣": [T: "láthatatlan elválasztó"] # 0x2063 - "⁤": [T: "és"] # 0x2064 (en: 'and', DeepL translation) - - "‴": [T: "hármas prím"] # 0x2034 (en: 'triple prime', DeepL translation) - "ℂℕℚℝℤ": # here we rely on this running through the table again to speak "cap xxx" - T: "duplán leütött" # (en: 'double-struck', DeepL translation) @@ -418,7 +417,6 @@ then: [T: "benne van ebben:"] # (en: 'is in', DeepL translation) - else: [T: "ebbe a halmazba tartozik:"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'belongs to', DeepL translation) - "∐": [T: "diszjunkt unió"] # 0x2210 (en: 'co-product', DeepL translation) - - "∗": [T: "csillag operátortimes"] # 0x2217 (DeepL translation) - "√": # 0x221a - test: if: "$Verbosity!='Terse'" @@ -448,7 +446,6 @@ if: "$Verbosity!='Terse'" then: [T: "van"] # (en: 'is', DeepL translation) - T: "hogy" # (DeepL translation) - - "∷": [T: "arány"] # 0x2237 (DeepL translation) - "∾": # 0x223e - test: if: "$Verbosity!='Terse'" @@ -474,8 +471,6 @@ if: "$Verbosity!='Terse'" then: [T: ""] # (en: 'is', DeepL translation) - T: "nagyobb vagy egyenlő" # (en: 'greater than or equal to', DeepL translation) - - "≦": [T: "kisebb, mint egyenlő"] # 0x2266 (en: 'less than over equal to', DeepL translation) - - "≧": [T: "nagyobb, mint az egyenlő"] # 0x2267 (en: 'greater than over equal to', DeepL translation) - "⊂": # 0x2282 - test: if: "$Verbosity!='Terse'" diff --git a/tests/Languages/hu.rs b/tests/Languages/hu.rs index 45bce3f9c..ac24dd22e 100644 --- a/tests/Languages/hu.rs +++ b/tests/Languages/hu.rs @@ -15,9 +15,9 @@ mod ClearSpeak { mod SimpleSpeak { mod functions; mod large_ops; - // mod menclose; + mod menclose; mod mfrac; - // mod mroot; + mod mroot; mod msup; mod sets; mod geometry; @@ -29,6 +29,7 @@ mod shared; mod units; mod chemistry; mod alphabets; +mod definitions; mod intent; mod mtable; diff --git a/tests/Languages/hu/ClearSpeak/functions.rs b/tests/Languages/hu/ClearSpeak/functions.rs index e9449b213..3dd6e94e7 100644 --- a/tests/Languages/hu/ClearSpeak/functions.rs +++ b/tests/Languages/hu/ClearSpeak/functions.rs @@ -516,7 +516,7 @@ fn explicit_times_none_superscript() -> Result<()> { ) "; test_ClearSpeak("hu", "ClearSpeak_Paren", "Interval",expr, - "c től d ig terjedő intervallum, nem tartalmazza a c elemet vagy d elemet")?; + "c től d ig terjedő intervallum, nem tartalmazza a(z) c elemet vagy d elemet")?; return Ok(()); } @@ -530,7 +530,7 @@ fn explicit_times_none_superscript() -> Result<()> { ) "; test_ClearSpeak("hu", "ClearSpeak_Paren", "Interval ",expr, - "c től d ig terjedő intervallum, tartalmazza a c elemet de nem tartalmazza a d elemet")?; + "c től d ig terjedő intervallum, tartalmazza a(z) c elemet de nem tartalmazza a(z) d elemet")?; return Ok(()); } @@ -545,7 +545,7 @@ fn parens_interval_open_closed() -> Result<()> { ] "; test_ClearSpeak("hu", "ClearSpeak_Paren", "Interval ",expr, - "c től d ig terjedő intervallum, nem tartalmazza a c elemet de tartalmazza a d elemet")?; + "c től d ig terjedő intervallum, nem tartalmazza a(z) c elemet de tartalmazza a(z) d elemet")?; return Ok(()); } @@ -560,7 +560,7 @@ fn parens_interval_closed_closed() -> Result<()> { ] "; test_ClearSpeak("hu", "ClearSpeak_Paren", "Interval ",expr, -"c től d ig terjedő intervallum, tartalmazza a c elemet és d elemet")?; +"c től d ig terjedő intervallum, tartalmazza a(z) c elemet és d elemet")?; return Ok(()); } @@ -573,7 +573,7 @@ return Ok(()); ) "; test_ClearSpeak("hu", "ClearSpeak_Paren", "Interval ",expr, - "negatív végtelen től d ig terjedő intervallum, nem tartalmazza a d elemet")?; + "negatív végtelen től d ig terjedő intervallum, nem tartalmazza a(z) d elemet")?; return Ok(()); } @@ -586,7 +586,7 @@ return Ok(()); ] "; test_ClearSpeak("hu", "ClearSpeak_Paren", "Interval ",expr, - "negatív végtelen től d ig terjedő intervallum, tartalmazza a d elemet")?; + "negatív végtelen től d ig terjedő intervallum, tartalmazza a(z) d elemet")?; return Ok(()); } @@ -601,7 +601,7 @@ fn parens_interval_open_open_infinity() -> Result<()> { ) "; test_ClearSpeak("hu", "ClearSpeak_Paren", "Interval ",expr, - "c től végtelen ig terjedő intervallum, nem tartalmazza a c elemet")?; + "c től végtelen ig terjedő intervallum, nem tartalmazza a(z) c elemet")?; return Ok(()); } @@ -616,7 +616,7 @@ fn parens_interval_closed_open_infinity() -> Result<()> { ) "; test_ClearSpeak("hu", "ClearSpeak_Paren", "Interval ",expr, -"c től végtelen ig terjedő intervallum, tartalmazza a c elemet")?; +"c től végtelen ig terjedő intervallum, tartalmazza a(z) c elemet")?; return Ok(()); } diff --git a/tests/Languages/hu/SimpleSpeak/functions.rs b/tests/Languages/hu/SimpleSpeak/functions.rs index aa4c2a43a..8ecf02cb8 100644 --- a/tests/Languages/hu/SimpleSpeak/functions.rs +++ b/tests/Languages/hu/SimpleSpeak/functions.rs @@ -355,7 +355,7 @@ fn no_times_sqrt() -> Result<()> { (c,d) ) "; - test("hu", "SimpleSpeak",expr, "c től d ig terjedő intervallum, nem tartalmazza a c elemet vagy d elemet")?; + test("hu", "SimpleSpeak",expr, "c től d ig terjedő intervallum, nem tartalmazza a(z) c elemet vagy d elemet")?; return Ok(()); } @@ -368,7 +368,7 @@ fn no_times_sqrt() -> Result<()> { [(]c,d) ) "; - test("hu", "SimpleSpeak",expr, "c től d ig terjedő intervallum, tartalmazza a c elemet de nem tartalmazza a d elemet")?; + test("hu", "SimpleSpeak",expr, "c től d ig terjedő intervallum, tartalmazza a(z) c elemet de nem tartalmazza a(z) d elemet")?; return Ok(()); } @@ -382,7 +382,7 @@ fn parens_interval_open_closed() -> Result<()> { (c,d] ] "; - test("hu", "SimpleSpeak",expr,"c től d ig terjedő intervallum, nem tartalmazza a c elemet de tartalmazza a d elemet")?; + test("hu", "SimpleSpeak",expr,"c től d ig terjedő intervallum, nem tartalmazza a(z) c elemet de tartalmazza a(z) d elemet")?; return Ok(()); } @@ -396,7 +396,7 @@ fn parens_interval_closed_closed() -> Result<()> { [(]c,d] ] "; - test("hu", "SimpleSpeak",expr, "c től d ig terjedő intervallum, tartalmazza a c elemet és d elemet")?; + test("hu", "SimpleSpeak",expr, "c től d ig terjedő intervallum, tartalmazza a(z) c elemet és d elemet")?; return Ok(()); } @@ -409,7 +409,7 @@ fn parens_interval_closed_closed() -> Result<()> { ) "; test("hu", "SimpleSpeak",expr, - "negatív végtelen től d ig terjedő intervallum, nem tartalmazza a d elemet")?; + "negatív végtelen től d ig terjedő intervallum, nem tartalmazza a(z) d elemet")?; return Ok(()); } @@ -422,7 +422,7 @@ fn parens_interval_closed_closed() -> Result<()> { ] "; test("hu", "SimpleSpeak",expr, - "negatív végtelen től d ig terjedő intervallum, tartalmazza a d elemet")?; + "negatív végtelen től d ig terjedő intervallum, tartalmazza a(z) d elemet")?; return Ok(()); } diff --git a/tests/Languages/hu/SimpleSpeak/menclose.rs b/tests/Languages/hu/SimpleSpeak/menclose.rs new file mode 100644 index 000000000..33c03f699 --- /dev/null +++ b/tests/Languages/hu/SimpleSpeak/menclose.rs @@ -0,0 +1,266 @@ +use crate::common::*; +use anyhow::Result; + +// AI generated +#[test] +fn menclose_actuarial() -> Result<()> { + let expr = " + 3+2i + "; + test("hu", "ClearSpeak", expr, "aktuárius szimbólum, kezdet: 3 plusz 2 i végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_box() -> Result<()> { + let expr = " + 3+2i + "; + test("hu", "ClearSpeak", expr, "doboz, kör, kezdet: 3 plusz 2 i végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_left() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "vonal balra, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_right() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "vonal jobbra, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_top_bottom() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "vonal felső, alsó, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_updiagonalstrike() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "átlósan felfelé, kihúzva, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_downdiagonalstrike() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "átlósan lefelé, kihúzva, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_cross_out() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "x, kihúzva, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_vertical_horizontal_strike() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "függőleges, vízszintes, kihúzva, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_leftarrow() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "balra nyíl, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_right_up_down_arrow() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "felfelé nyíl, lefelé nyíl, jobbra nyíl, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_northeastarrow() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "északkeleti nyíl, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_other_single_arrows() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "délkeleti nyíl, délnyugati nyíl, északnyugati nyíl, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_northwestsoutheastarrow() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "kétvégű lefelé mutató átlós nyíl, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_other_double_arrows() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "kétvégű függőleges nyíl, kétvégű vízszintes nyíl, dupla végű átlós nyíl, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_madrub() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "arab faktoriális szimbólum, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_phasorangle() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "fázisszög, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_circle_phasorangle() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "kör, fázisszög, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_longdiv() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "hosszú osztásjel, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_longdiv_default() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "hosszú osztásjel, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_longdiv_empty_string() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "hosszú osztásjel, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_longdiv_whitespace_string() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "hosszú osztásjel, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn menclose_radical() -> Result<()> { + let expr = " + 32 + "; + test("hu", "ClearSpeak", expr, "négyzetgyök, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn simple_speak_menclose_top_bottom() -> Result<()> { + let expr = " + 32 + "; + test("hu", "SimpleSpeak", expr, "vonal felső, alsó, kezdet: 3 ketted végpont vége")?; + return Ok(()); + +} diff --git a/tests/Languages/hu/SimpleSpeak/mroot.rs b/tests/Languages/hu/SimpleSpeak/mroot.rs new file mode 100644 index 000000000..225e191c0 --- /dev/null +++ b/tests/Languages/hu/SimpleSpeak/mroot.rs @@ -0,0 +1,178 @@ +use crate::common::*; +use anyhow::Result; + +// AI generated +#[test] +fn msqrt_simple() -> Result<()> { + let expr = " + x + "; + test("hu", "SimpleSpeak", expr, "a négyzetgyöke ennek: x")?; + return Ok(()); + +} + +// AI generated +#[test] +fn msqrt_simple_end_root() -> Result<()> { + let expr = " + x + "; + test_ClearSpeak("hu", "ClearSpeak_Roots", "RootEnd", expr, "négyzetgyök x, gyök vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn msqrt_simple_positive() -> Result<()> { + let expr = " + x + "; + test_ClearSpeak("hu", "ClearSpeak_Roots", "PosNegSqRoot", expr, "pozitív négyzetgyök x")?; + return Ok(()); + +} + +// AI generated +#[test] +fn msqrt_simple_pos_end_root() -> Result<()> { + let expr = " + x + "; + test_ClearSpeak("hu", "ClearSpeak_Roots", "PosNegSqRootEnd", expr, "pozitív négyzetgyök x, gyök vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn msqrt_simple_pos_end_with_neg_root() -> Result<()> { + let expr = " + - x + - x 3 + "; + test_ClearSpeak("hu", "ClearSpeak_Roots", "PosNegSqRootEnd", expr, + "negatív négyzetgyök x, gyök vége; mínusz, a pozitív köbgyök x, gyök vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn mroot_simple_pos_end_with_neg_root() -> Result<()> { + let expr = " + - x 3 + - x + + "; + test_ClearSpeak("hu", "ClearSpeak_Roots", "PosNegSqRoot", expr, + "a negatív köbgyök x; mínusz pozitív négyzetgyök x")?; + return Ok(()); + +} + +// AI generated +#[test] +fn neg_without_root() -> Result<()> { + let expr = " + - x - y + "; + test("hu", "ClearSpeak", expr, "negatív x mínusz y")?; + return Ok(()); + +} + +// AI generated +#[test] +fn msqrt() -> Result<()> { + let expr = " + + x + y + + "; + test("hu", "ClearSpeak", expr, "négyzetgyök x plusz y")?; + return Ok(()); + +} + +// AI generated +#[test] +fn mroot_as_square_root() -> Result<()> { + let expr = " + x 2 + "; + test("hu", "ClearSpeak", expr, "négyzetgyök x")?; + return Ok(()); + +} + +// AI generated +#[test] +fn cube_root() -> Result<()> { + let expr = " + x 3 + "; + test("hu", "ClearSpeak", expr, "a köbgyök x")?; + return Ok(()); + +} + +// AI generated +#[test] +fn ordinal_root() -> Result<()> { + let expr = " + x 9 + "; + test("hu", "ClearSpeak", expr, "a kilencedik gyök x")?; + return Ok(()); + +} + +// AI generated +#[test] +fn simple_mi_root() -> Result<()> { + let expr = " + x n + "; + test("hu", "ClearSpeak", expr, "a n-edik gyök x")?; + return Ok(()); + +} + +// AI generated +#[test] +fn mroot_simple_pos_end_root() -> Result<()> { + let expr = " + x t + "; + test_ClearSpeak("hu", "ClearSpeak_Roots", "PosNegSqRootEnd", expr, "a pozitív t-edik gyök x, gyök vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn mroot_simple_end_root() -> Result<()> { + let expr = " + x + y + 21 + "; + test_ClearSpeak("hu", "ClearSpeak_Roots", "RootEnd", expr, "a huszonegyedik gyök x plusz y, gyök vége")?; + return Ok(()); + +} + +// AI generated +#[test] +fn simple_fraction_power() -> Result<()> { + let expr = " + + x + 13 + + "; + test("hu", "ClearSpeak", expr, "a 1 harmad gyök x")?; + return Ok(()); + +} diff --git a/tests/Languages/hu/definitions.rs b/tests/Languages/hu/definitions.rs new file mode 100644 index 000000000..bd84fb155 --- /dev/null +++ b/tests/Languages/hu/definitions.rs @@ -0,0 +1,1026 @@ +/// Tests for rules in definitions: +/// * modified var +use crate::common::*; +use anyhow::Result; + +#[test] +fn tuple_basic() -> Result<()> { + // function + let expr = r#" + + + x + y + + + "#; + + test("hu", "ClearSpeak", expr, "a tuple x vessző, y")?; + test("hu", "SimpleSpeak", expr, "a tuple x vessző, y")?; + + return Ok(()); +} + +#[test] +fn my_set_basic() -> Result<()> { + let expr = r#" + + + x + y + + + "#; + + test("hu", "ClearSpeak", expr, "az üres halmaz")?; + + Ok(()) +} + +#[test] +fn fixed_test() -> Result<()> { + let expr = r#" + + az összes valós szám halmaza + + + "#; + + test("hu", "ClearSpeak", expr, "az összes valós szám halmaza")?; + Ok(()) +} + +#[test] +fn i_test() -> Result<()> { + let expr = r#" + + képzeletbeli én + + + "#; + + test("hu", "ClearSpeak", expr, "képzeletbeli én")?; + Ok(()) +} + + +#[test] +fn floor_basic() -> Result<()> { + let expr = r#" + + + x + + + "#; + + test("hu", "ClearSpeak", expr, "lépcső x")?; + + Ok(()) +} + +#[test] +fn set_difference_basic() -> Result<()> { + let expr = r#" + + + A + ∖ + B + + + "#; + + test( "hu", "ClearSpeak", expr, "különbséghalmaz nagy a vessző, nagy b")?; + + Ok(()) +} + +#[test] +fn postfix_test() -> Result<()> { + let tests = [ + ( + "transpose", + r#" + + x + T + + "#, + "transzponált x", + ), + ( + "highlight", + r#" + + x + + "#, + "x kiemelt", + ), + ]; + + // Loop through all test cases, name _, body, and expected result + for (_, body, expected) in tests { + let expr = format!( + r#" + + {} + + "#, + body + ); + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + +#[test] +fn prefix_test() -> Result<()> { + // Prefix test limit, unit-vector, line-segment + // Directed-line-segment, line, ray, arc + let tests = [ + ( + "limit", + r#" + + x + + "#, + "határérték x", + ), + ( + "unit-vector", + r#" + + x + ^ + + "#, + "egységvektor x", + ), + ( + "line-segment", + r#" + + x + ¯ + + "#, + "line segment x", + ), + ]; + + for (_, body, expected) in tests { + let expr = format!( + r#" + + {} + + "#, + body + ); + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + + +#[test] +fn functions_and_inverses_tests() -> Result<()> { + let tests = vec![ + ("closed-interval", "x től y ig terjedő intervallum, tartalmazza a(z) x elemet és y elemet"), + ("closed-open-interval", "x től y ig terjedő intervallum, tartalmazza a(z) x elemet de nem tartalmazza a(z) y elemet"), + ("open-closed-interval", "x től y ig terjedő intervallum, nem tartalmazza a(z) x elemet de tartalmazza a(z) y elemet"), + ("open-interval", "x től y ig terjedő intervallum, nem tartalmazza a(z) x elemet vagy y elemet"), + + ("inverse", "inverz x"), + ("domain", "domén x"), + ("codomain", "kodomán x"), + ("image", "kép x"), + + //("fraction", "fraction x over y end fraction"), + ("mixed-fraction", "x és y"), + ("quotient", "egész rész osztva x vessző, y"), + ("evaluated-at", "x értékelve y"), + ("remainder", "a maradék, osztva x vessző, y"), + + ("max", "max x vessző, y vessző, z"), + ("min", "min x vessző, y vessző, z"), + + ("power", "x a(z) y-edik hatványon"), + + //("root", "gyök x"), + + ("greatest-common-divisor", "a legnagyobb közös osztó x vessző, y vessző, z"), + ("least-common-multiple", "a legkisebb közös többszörös x vessző, y vessző, z"), + + ("absolute-value", "x abszolút értéke"), + + ("complex-conjugate", "Komplex konjugált x"), + ("complex-arg", "argumentum x"), + ("real-part", "valós rész"), + ("imaginary-part", "képzetes rész"), + + ("polar-coordinate", "poláris koordináta x vessző, y"), + ("spherical-coordinate", "gömb koordináta x vessző, y vessző, z"), + ("cartesian-coordinate", "derékszögű koordináta x vessző, y vessző, z"), + ("coordinate", "pont, x vessző y vessző z"), + + ("floor", "lépcső x"), + ("ceiling", "mennyezet x"), + ("round", "kerekített érték x"), + ("fractional-part", "tört rész x"), + + + ]; + + for (intent, expected) in tests { + let expr = match intent { + + "max" + | "min" + | "greatest-common-divisor" + | "least-common-multiple" + | "spherical-coordinate" + | "cartesian-coordinate" + | "coordinate" => { + format!( + " + + x + y + z + + ", + intent + ) + } + + "power" + | "mixed-fraction" + | "quotient" + | "evaluated-at" + | "remainder" + | "closed-interval" + | "closed-open-interval" + | "open-closed-interval" + | "open-interval" + | "polar-coordinate" => { + format!( + " + + x + y + + ", + intent + ) + } + + _ => { + format!( + " + + x + + ", + intent + ) + } + }; + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + + +#[test] +fn calculus_tests() -> Result<()> { + let tests = vec![ + //("derivative", "the derivative of x with respect to x"), + //"definite-integral", "integral over x from x to x"), + + // prefix + ("limit", "határérték x"), + + // infix + ("tends-to", "x ehhez tart y"), + ("tends-to-from-above", "x általában alulról indul y"), + ("tends-to-from-below", "x alulról indul y"), + ]; + + for (intent, expected) in tests { + let expr = match intent { + "limit" => { + format!( + " + + x + + ", + intent + ) + } + + _ => { + // infix cases + format!( + " + + x + y + + ", + intent + ) + } + }; + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + + +#[test] +fn set_tests() -> Result<()> { + let tests = vec![ + ("set", "a halmaz x"), + //("set-difference", "x és y halmaz különbsége"), + ("complement", "komplemens x"), + //("empty-set", "üres halmaz"), + ("cardinality", "számosság x"), + ("list", "lista x"), + ("tuple", "a tuple x"), + ]; + + for (intent, expected) in tests { + let expr = format!( + " + + x + + ", + intent + ); + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + + +#[test] +fn sequence_and_series_intents() -> Result<()> { + let tests = [ + ( + "sum-1", + r#" + + ∑ + x + + "#, + "összeg x", + ), + ( + "sum-2", + r#" + + ∑ + i + x + + "#, + "összeg i vessző, x", + ), + ( + "sum-3", + r#" + + ∑ + i + n + x + + "#, + "összeg i vessző, n vessző, x", + ), + ( + "product-1", + r#" + + ∏ + x + + "#, + "szorzat x", + ), + ( + "product-2", + r#" + + ∏ + i + x + + "#, + "szorzat i vessző, x", + ), + ( + "product-3", + r#" + + ∏ + i + n + x + + "#, + "szorzat i vessző, n vessző, x", + ), + ]; + + for (_, body, expected) in tests { + let expr = format!( + r#" + + {} + + "#, + body + ); + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + +#[test] +fn elementary_classical_tests() -> Result<()> { + let tests = vec![ + // Trig + ("sine", "szinusz x"), + ("cosine", "koszinusz x"), + ("tangent", "tangens x"), + ("secant", "szekáns x"), + ("cosecant", "koszekáns x"), + ("cotangent", "kotangens x"), + + // Inverse trig + ("arcsine", "arkusz szinusz x"), + ("arccosine", "arkusz koszinusz x"), + ("arctangent", "arkusz tangens x"), + ("arcsecant", "arkusz szekáns x"), + ("arccosecant", "arkusz koszekáns x"), + ("arccotangent", "arkusz kotangens x"), + + // Hyperbolic trig + ("hyperbolic-sine", "hiperbolikus szinusz x"), + ("hyperbolic-cosine", "hiperbolikus koszinusz x"), + ("hyperbolic-tangent", "hiperbolikus tangens x"), + ("hyperbolic-secant", "hiperbolikus szekáns x"), + ("hyperbolic-cosecant", "hiperbolikus koszekáns x"), + ("hyperbolic-cotangent", "hiperbolikus kotangens x"), + + // Inverse hyperbolic trig + ("arc-hyperbolic-sine", "arkusz hiperbolikus szinusz x"), + ("arc-hyperbolic-cosine", "arkusz hiperbolikus koszinusz x"), + ("arc-hyperbolic-tangent", "arkusz hiperbolikus tangens x"), + ("arc-hyperbolic-secant", "arkusz hiperbolikus szekáns x"), + ("arc-hyperbolic-cosecant", "arkusz hiperbolikus koszekáns x"), + ("arc-hyperbolic-cotangent", "arkusz hiperbolikus kotangens x"), + ]; + + for (intent, expected) in tests { + let expr = format!( + " + + x + + ", + intent + ); + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + +#[test] +fn statistics_and_probability_tests() -> Result<()> { + let tests = vec![ + ("mean", "jelentése x"), + ("standard-deviation", "szórás x"), + ("variance", "variencia x"), + ("median", "medián x"), + ("mode", "mód x"), + + // conditional probability typically two arguments + // ("conditional-probability", "probability of x given y"), + ]; + + for (intent, expected) in tests { + let expr = match intent { + "conditional-probability" => format!( + " + + P + ( + + x + | + y + + ) + + ", + intent + ), + _ => format!( + " + + x + + ", + intent + ), + }; + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + +#[test] +fn linear_algebra_tests() -> Result<()> { + let tests = vec![ + ("vector", "vektor x"), + // ("matrix", "matrix of x"), + ("determinant", "determináns a x"), + ("adjugate", "adjugált x"), + ("magnitude", "nagyságrend x"), + ("norm", "normalizálás x"), + ("span", "fesztávolság x"), + + // transpose supports both postfix and function; we test function explicitly + ("transpose", "transzponált x"), + + // dimensional product is infix + ("dimensional-product", "x a(z) y"), + + // unit-vector is prefix + ("unit-vector", "egységvektor x") + ]; + + for (intent, expected) in tests { + let expr: String = match intent { + "dimensional-product" => { + " + + x + y + + " + .to_string() + } + _ => format!( + " + + x + + " + ), + }; + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + +#[test] +fn nofix_set_tests() -> Result<()> { + let tests = vec![ + ("set-of-integers", "ℤ", "az összes egész szám halmaza"), + ("set-of-reals", "valós számok halmaza", "az összes valós szám halmaza"), + ("set-of-rationals", "ℚ", "az összes racionális szám halmaza"), + ("set-of-natural-numbers", "ℕ", "az összes természetes szám halmaza"), + ("set-of-complex-numbers", "ℂ", "az összes komplex szám halmaza"), + ("set-of-primes", "ℙ", "az összes prímszám halmaza"), + ]; + + for (intent, symbol, expected) in tests { + let expr = format!( + " + {} + ", + intent, + symbol + ); + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + +#[test] +fn geometry_prefix_multi_value_tests() -> Result<()> { + let tests = vec![ + ("line-segment", "vonalszakasz x y"), + ("directed-line-segment", "irányított vonalszakasz x y"), + ("line", "vonal x y"), + ("ray", "sugár x y"), + ("arc", "ív x y"), + ("point", "pont x y z"), + ]; + + for (intent, expected) in tests { + let expr = match intent{ + "point" => { + format!( + " + + x + y + z + + " + ) + } + + _ => { + format!( + " + + x + y + + " + ) + } + }; + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + +#[test] +fn geometry_prefix_tests() -> Result<()> { + let tests = vec![ + ("length", "hossz x"), + ("area", "terület x"), + ("volume", "hangerő x"), + ]; + + for (intent, expected) in tests { + let expr = format!( + " + + x + + " + ); + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + +#[test] +fn separator_tests() -> Result<()> { + let expr = format!( + " + + x + y + + " + ); + + test("hu", "ClearSpeak", &expr, "x y")?; + Ok(()) +} + +#[test] +fn general_concepts_tests() -> Result<()> { + let tests = vec![ + // Unary structural + ("fenced-group", "bekerített csoport x"), + ("highlight", "x kiemelt"), + ("least-common-denominator", "legkisebb közös nevező x vessző, y vessző, z"), // add x, y , z ... + ("pochhammer", "permutáció x"), + ("permutation-cycle", "permutációs ciklus x"), + + // Binary structural / infix-style + // ("ordered-pair", "the pair of x and y"), + ("rate", "x per y"), + + ("binomial-coefficient", "x választ y"), + ("embellished-name", "x megjegyzéssel y"), + ("indexed-by", "x alsó index y"), + // ("translation", "translation by x comma, y"), // Changes translation to comma + ("constraint", "x feltétellel y"), + ]; + + for (intent, expected) in tests { + let expr = match intent { + "ordered-pair" + | "rate" + | "constraint" + | "binomial-coefficient" + | "embellished-name" + | "translation" + | "indexed-by" => { + format!( + " + + x + y + + ", + intent + ) + } + "least-common-denominator" => { + format!( + " + + x + y + z + + ", + intent + ) + } + + // unary cases + _ => { + format!( + " + + x + + ", + intent + ) + } + }; + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + +#[test] +fn grouping_tests() -> Result<()> { + let tests = vec![ +("annotation", "x ami y"), + // ("braced-group", "grouped x end-grouped"), + // ("repeating-decimal", "repeating decimal of x"), + ]; + + for (intent, expected) in tests { + let expr = match intent { + "annotation" => { + format!( + " + + x + y + + ", + intent + ) + } + _ => { + format!( + " + + x + + ", + intent + ) + } + }; + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + +#[test] +fn function_default_fixity_tests() -> Result<()> { + let tests = vec![ + ("curl", "rotáció x"), + ("divergence", "eltérés x"), + ("gradient", "gradiens x"), + ("laplacian", "laplaciánus x"), + ]; + + for (intent, expected) in tests { + let expr = format!( + " + + x + + ", + intent + ); + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + +#[test] +fn prefix_default_fixity_tests() -> Result<()> { + let tests = vec![ + ("angle", "szög x"), + ("angle-measure", "szögmérés x"), + ("change", "változás x"), + ("for-all", "minden x"), + ("measured-angle", "mért szög x"), + ("not", "nem x"), + ("number-of", "száma x"), + ("partial-derivative", "parciális derivált x"), + ("right-angle", "derékszög x"), + ("square-root-of", "négyzetgyöke x"), + ("there-does-not-exist", "nem létezik x"), + ("there-exists", "létezik x"), + ]; + + for (intent, expected) in tests { + let expr = format!( + " + + x + + ", + intent + ); + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + +#[test] +fn infix_default_fixity_tests() -> Result<()> { + let tests = vec![ + ("and", "x és y"), + ("applied-to", "x alkalmazva y"), + ("approximately", "x hozzávetőlegesen y"), + ("congruent", "x egybeesik y"), + ("cartesian-product", "x derékszögű szorzat y"), + ("composed-with", "x komponálva y"), + ("cross-product", "x kereszt szorzata y"), + ("defined-as", "x definiálva y"), + ("divided-by", "x osztva y"), + ("divides", "x osztja y"), + ("does-not-belong-to", "x nem tartozik hozzá y"), + ("does-not-divide", "x nem osztja y"), + ("dot-product", "x skaláris szorzat y"), + ("downwards-diagonal-ellipsis", "x lefelé mutató átlós ellipszis y"), + ("direct-product", "x direkt szorzat y"), + ("element-of", "x eleme y"), + ("ellipsis", "x ellipszis y"), + ("equals", "x egyenlő y"), + ("equivalent-to", "x egyenértékű ezzel y"), + ("evaluates-to", "x értéke y"), + ("given", "x adott y"), + ("greater-than", "x nagyobb, mint y"), + ("greater-than-or-equal-to", "x nagyobb vagy egyenlő, mint y"), + ("identically-equals", "x teljesen egyenlő y"), + ("if-and-only-if", "x akkor és csak akkor, ha y"), + ("implies", "x utal y"), + ("inner-product", "x belső szorzat y"), + ("intersection", "x keresztezi y"), + ("less-than", "x kisebb, mint y"), + ("less-than-or-equal-to", "x kisebb vagy egyenlő, mint y"), + ("list-separator", "x vessző y"), + ("maps-to", "x megfelel ennek y"), + ("member-of", "x tagja y"), + ("minus", "x mínusz y"), + ("minus-or-plus", "x mínusz vagy plusz y"), + ("not-subset", "x nem részhalmaza y"), + ("not-superset", "x nem szuperhalmaza y"), + ("not-equal-to", "x nem egyenlő y"), + ("not-member-of", "x nem tagja y"), + ("not-parallel-to", "x nem párhuzamos y"), + ("obtained-from", "x innen származik y"), + ("or", "x vagy y"), + ("outer-product", "x külső szorzata y"), + ("parallel-to", "x párhuzamos y"), + ("perpendicular", "x merőleges y"), + ("plus", "x plusz y"), + ("plus-or-minus", "x plusz vagy mínusz y"), + ("precedes", "x megelőzi y"), + ("proportional", "x arányos y"), + ("range-separator", "x keresztül y"), + ("ratio", "x arány y"), + ("similar", "x hasonló y"), + ("subset", "x részhalmaza y"), + ("subset-or-equal", "x részhalmaza vagy egyenlő y"), + ("succeeds", "x követi y"), + ("such-that", "x úgy, hogy y"), + ("superset", "x szuperhalmaza y"), + ("superset-or-equal", "x szuperhalmaza vagy egyenlő y"), + ("tilde", "x tilde y"), + ("times", "x szorozva y"), + ("union", "x unió y"), + ("upwards-diagonal-ellipsis", "x felfelé átlós ellipszis y"), + ("vertical-ellipsis", "x függőleges ellipszis y"), + ("xor", "x kizárólagos vagy y"), + ]; + + for (intent, expected) in tests { + let expr = format!( + " + + x + y + + ", + intent + ); + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + +#[test] +fn postfix_default_fixity_tests() -> Result<()> { + let tests = vec![ + ("factorial", "x faktoriális"), + ("percent", "x százalék"), + ]; + + for (intent, expected) in tests { + let expr = format!( + " + + x + + ", + intent + ); + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} + +#[test] +fn nofix_default_fixity_tests() -> Result<()> { + let tests = vec![ + ("diameter", "átmérő", "átmérő"), + ("distance", "távolság", "távolság"), + ("probability", "valószínűség", "valószínűség"), + ("radius", "sugár", "sugár"), + ("volume", "hangerő", "hangerő"), + ("exponential-e", "e", "e"), + ("imaginary-i", "képzeletbeli én", "képzeletbeli én"), + ("differential-d", "d", "d"), + ("golden-ratio", "φ", "aranymetszés"), + ]; + + for (intent, symbol, expected) in tests { + let expr = format!( + " + {} + ", + intent, + symbol + ); + + test("hu", "ClearSpeak", &expr, expected)?; + } + + Ok(()) +} diff --git a/tests/Languages/hu/shared.rs b/tests/Languages/hu/shared.rs index a97805f8a..bd53bb4c3 100644 --- a/tests/Languages/hu/shared.rs +++ b/tests/Languages/hu/shared.rs @@ -381,7 +381,7 @@ fn bug_199_2pi() -> Result<()> { ) "; - test("hu", "SimpleSpeak",expr, "0 től 2 pí ig terjedő intervallum, tartalmazza a 0 elemet de nem tartalmazza a 2 pí elemet")?; + test("hu", "SimpleSpeak",expr, "0 től 2 pí ig terjedő intervallum, tartalmazza a(z) 0 elemet de nem tartalmazza a(z) 2 pí elemet")?; return Ok(()); } From 7fbaf1729eadac816de837af931e799f1dceb6c9 Mon Sep 17 00:00:00 2001 From: NSoiffer Date: Sat, 15 Aug 2026 01:17:17 -0700 Subject: [PATCH 029/120] Ru -- merge speech changes into main (#689) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Ru - Improve wording of Russian docs (#661) * update acknowledgment for Russian braille * update version number because added Russian braille * UEB has a rule for some pseudo-script chars. The match for that rule was overly broad, so I removed scripted letters and numbers. That rule made its way into other braille codes, so I removed them everywhere. All tests pass. * Added new UEB chars. Many of these are accented letters. I added 'A' (for accented letters) so that the pattern for them looks like: ``` - "Ñ": [t: "CA⠘⠻L⠝"] # 0x00D1 ``` Added a few tests. * Translate German absolute-value ending * Fix German unit plurals * Fix German pluralization for "ruthe" and add regression test * Correct German translations for "ruthe" to "rute" and update regression test * Expand German unit definitions and add regression test for pluralization * fix some symbols * fix german set grammar * typo in example * Russian Braille fixes This commit adds fixes to Russian Braille. Now the test with parentheses is passed. Also there are devision and multiplication signs have been modified according to the Russian braille specs. * Update Russian Braille rules and mappings Fix YAML replace syntax and extend Russian braille rules: add chemical-formula-operator-bond and prefix-geometry-ops rules, broaden function-name matching (include tg/ctg variants) and add separator markers. Update unicode mappings (degree sign, invisible characters, conditional ∪ mapping, parallel/arrow glyphs) and adjust several function/operator braille outputs. Add/expand tests to cover functions, geometry, matrices and chemical formulas to validate the changes. * Add Russian language to docs Add Russian (ru) to the list of supported speech languages and update Russian documentation and translation guidance. - docs/User_guide_for_MathCAT_en.md, docs/index.md: add Russian (ru) to supported languages lists. - docs/ru/User_guide_for_MathCAT_ru.md: include Russian in language options, reorder entries and apply minor wording fixes. - docs/ru/helpers.md: clarify wording, update examples and test instructions to reference ru instead of fr, and improve translation guidance and examples (including test and cargo commands). - docs/ru/index.md: reorder supported languages and polish phrasing. These edits enable and document Russian support and improve clarity for translators and developers working on the Russian localization. * Refactor Russian function braille mapping Replace hardcoded function-name branches in Russian_Rules.yaml with a DefinitionValue lookup that pulls from a new RussianFunctionBraille mapping. Add RussianFunctionBraille entries to definitions.yaml (private-use codepoints U+E100–U+E10E) and map those codepoints to braille strings in unicode.yaml. Add a test covering tan/cot rendering. This centralizes function braille representations and simplifies the rule logic. * Addition of Russian documentation This commit adds Russian documentation for users, developers and translators. * typo in example * Add vector/bar/arc mover rules and tests Introduce three new mover rules in Rules/Braille/Russian/Russian_Rules.yaml: vector-arrow, vector-bar, and geometry-arc. Each rule detects specific accent characters (arrows: → ⟶ ⇀ ⃗; bars: ¯ ‾ _  ̄; arcs: ⌒ ⏜ ︵) and emits the appropriate braille indicators, using a different form when the base has more than one character. Add corresponding tests in tests/braille/Russian/russian.rs to cover single- and multi-character movers and combined expressions. * Add Russian braille math functions and limits Extend Russian braille rules to support additional math functions and large operator limits. Added new rules for large-op-limits, operator/functions (including special handling for div), and div-specific replacements in Rules/Braille/Russian/*. Updated Russian definitions and unicode mappings to include sinh/cosh/tanh/coth, arg/sgn/det/rank/rg, Re/Im, grad/rot/div, gcd/lcm and related braille codes. Updated global Rules/definitions.yaml to recognize new function names. Fixed braille cleanup in src/braille.rs to normalize div encoding. Added unit tests exercising the new function mappings and large operator limits. * Support system left brace in Russian braille Introduce handling for system left-brace matrices in the Russian braille rules: add InSystemLeftBrace variable, a system-left-brace-opener rule and a system-left-brace-table rule to detect brace-enclosed mtable as a system. Update row-start logic to emit appropriate braille tokens when InSystemLeftBrace is set. Add tests exercising curly-brace matrices, piecewise function (brace with conditions), and vertical-bar matrices to verify the new behavior. * Add signed-integer and nested-fraction rules Add handling for nested fractions and signed integers inside script positions and adjust grouping behavior for scripts in Russian braille rules. Changes introduce a nested-fraction rule for mfrac and new signed-integer-number / signed-integer rules for msub/munder and msup/mover, plus a small grouping test (⠐) when scripts contain grouped mrow with '+' or a fraction. Also add a comprehensive regression test (script_grouping_regressions) covering many sup/sub cases to ensure correct braille output. Files updated: Rules/Braille/Russian/Russian_Rules.yaml and tests/braille/Russian/russian.rs. * Enhance Russian Braille support with mathematical symbols and rules (#589) * Refine matrix rule handling by adding support for augmented matrices; update unit tests to include 2x3 and 3x4 augmented matrix scenarios. (#555) * remove logging statement * removed uneeded eprintlin!() * Alternative fix to PR #540. Fixes #528. Includes the tests in the PR. * Add Russian translation * Fix #585 -- SSML was being escaped. I earlier added code to prevent a SSML injection attack by escaping HTML. It was done at the wrong point and escape legal SSML. I have moved the check and conversion down to the leaves. I was very surprised there were no useful tests with SSML. All the main tests set `TTS=None`. I added a test that catches bad escaping. I also added some tests to check for leaf-based SSML attacks and also attribute-based ones. * bump beta number * The range for MATHEMATICAL SANS-SERIF DIGITs had MATHEMATICAL MONOSPACE DIGITs as the match part of translate, so if one of the sans-serif digits were used, you ended up in a loop (no match, to it returned itself). * comment out some debug statements * Add 02DA (˚) to list of circle-like chars -- I thought I had done this already, so it might in some other branch * Add source language option to translation audit tool Allow audit-translations to compare a target language against any source language via --source, defaulting to English for existing workflows. Update CLI output, docs, and tests for dynamic source/target labels, including Swedish/Norwegian comparison coverage. Normalize Rich ANSI output in tests so golden and string assertions remain stable when terminal color is forced. * fix ruff * fix ruff * fix ruff * Core concept names additions and tests -- issue 381 (#592) * initial addition of core concept names * defnitions and tests, with a few failing * minus, plus, volume fixes. Other defined, not appearing in site * fix clippy warnings * use "ruamel.yaml>=0.19.1" in uv.lock and pyproject.toml * handle canonicalize failure in containerized environments (#574) * add 1 week dependency cooldown for uv (#599) * fix rules for digits in german (#596) * update navigation rules for traditional chinese (#588) * copy english version of navigate.yaml from NVDA2026.1 and translate * 更新 definitions.yaml 與 navigate.yaml * update unicode-full.yaml * 區分完全/已完全放大/縮小 * 移至->移到, 表->表格, 數學式->數學, 退出->離開, 不能->無法, 當前->目前。 overview line 47, SimpleSpeak_Rules.yaml line 39, "根號"->""。mroot.rs 對應修改 * 移到上一格 -> 往上一格,移到下一格 -> 往下一格, 佔位符 -> 書籤,設定佔位符 -> 設定書籤,已是最上層 -> 不在別的式子裡面,上一層是 -> 在後面式子裡面,字元模式 開n 次方根 的讀法,基底 -> 基、底 * 向量 -> 射線 * 基 -> 基底,底 -> 基底 * Internally in definitions.yaml, MathCAT uses "nofix", but that's not a MathML airity property. Remove it. * don't say "indexed by", say "sub" * add some terse and verbose forms of speech * fix typo * Added an html file with all the test exprs so that translators have examples to test. * Fix remaining erros after `definitions.yaml` was updated -- needed to add `function:` * fix clippy warning * minor cleanup: remove ASCIIMath-fi reference * Refactor braille.rs to a BrailleCode trait + registry Replace the four scattered string-match dispatch sites (cleanup, get_braille_chars, NeedsToBeGrouped, and highlight knobs) with a single get_braille_code() registry and a BrailleCode trait implemented per code. Extract the duplicated UEB/Vietnam/Finnish/Swedish indicator-replacement closures into one apply_indicator_replacements helper plus a UserTypeforms struct. Behavior is unchanged (all tests/braille pass); adding a new code now means one registry entry + one impl block. Co-authored-by: Cursor * improved counting for (likey) 8 bit codes when figuring out braille position * make sure this is part of repo * add BrailleDocs to repo (stop ignoring) * updated data, does not include ASCII letters/digits * First version of Russian Braille This commit adds first druft version for Russian Braille. * typo in example * Russian Braille fixes This commit adds fixes to Russian Braille. Now the test with parentheses is passed. Also there are devision and multiplication signs have been modified according to the Russian braille specs. * Braille - Latin and Greek letters fixes This commit adds a layer of Latin and Greek alphabets for Russian mathematical Braille. * In unicode.yaml Latin and Greek letters now go through the internal markers of the alphabetic mode. • In braille.rs russian_cleanup turns these markers into real signs: ◦ Latin lowercase: ⠠ ◦ Latin uppercase: ⠨ ◦ Greek lowercase: ⠰ ◦ Greek uppercase: ⠸ Added tests to russian.rs to x+A+y+B, the Latin letter after the number. * Braille - Roots, powers and scripts This commit adds Roots, powers and scripts according to the Russian math Braille specs. * Update Russian Braille rules and mappings Fix YAML replace syntax and extend Russian braille rules: add chemical-formula-operator-bond and prefix-geometry-ops rules, broaden function-name matching (include tg/ctg variants) and add separator markers. Update unicode mappings (degree sign, invisible characters, conditional ∪ mapping, parallel/arrow glyphs) and adjust several function/operator braille outputs. Add/expand tests to cover functions, geometry, matrices and chemical formulas to validate the changes. * Add Russian language to docs Add Russian (ru) to the list of supported speech languages and update Russian documentation and translation guidance. - docs/User_guide_for_MathCAT_en.md, docs/index.md: add Russian (ru) to supported languages lists. - docs/ru/User_guide_for_MathCAT_ru.md: include Russian in language options, reorder entries and apply minor wording fixes. - docs/ru/helpers.md: clarify wording, update examples and test instructions to reference ru instead of fr, and improve translation guidance and examples (including test and cargo commands). - docs/ru/index.md: reorder supported languages and polish phrasing. These edits enable and document Russian support and improve clarity for translators and developers working on the Russian localization. * Refactor Russian function braille mapping Replace hardcoded function-name branches in Russian_Rules.yaml with a DefinitionValue lookup that pulls from a new RussianFunctionBraille mapping. Add RussianFunctionBraille entries to definitions.yaml (private-use codepoints U+E100–U+E10E) and map those codepoints to braille strings in unicode.yaml. Add a test covering tan/cot rendering. This centralizes function braille representations and simplifies the rule logic. * Refine Russian wording in translators guide Clarify and naturalize Russian phrasing in the translators guide: add explicit "на русском языке" to SimpleSpeak examples; change exponent wording to "в квадрате" with example "икс в квадрате"; make fraction wording more natural ("числитель разделить на знаменатель" and example "метр за секунду"); fix "английском" → "английском языке"; and update Braille tooling phrasing to "просмотрщиком Брайля" and "дисплее Брайля" for consistency. * Add alphabet indicators in russian_cleanup Insert a preprocessing step that injects braille alphabet indicators before running REPLACE_INDICATORS. A new helper add_russian_alphabet_indicators scans the raw braille and emits specific indicators for 'l' (Latin lowercase), 'u' (Latin uppercase), 'g' (Greek lowercase) and 'v' (Greek uppercase), avoiding repeated indicators by tracking the current alphabet mode. The helper also resets the mode on 'C', 'N', and '#'. This keeps the existing indicator replacement and space-collapsing logic intact while preventing redundant alphabet indicators. * Russian braille: add GOST symbols and decimals Add GOST-style number and symbol support for Russian braille: introduce a decimal-comma rule that emits BrailleChars for integer and fractional parts with a decimal indicator, using PUA digit placeholders. Extend unicode mappings with bracket/brace/slash/backslash/pipe characters, Celsius, context-sensitive prime (′/″) handling, PUA digit-to-braille mappings, and a few arrow/connective symbols. Update tests to cover decimal numbers, fractions, set notation, Celsius, and logical arrows, and adjust expected output for geometry primes to reflect the new context-aware prime handling. * Add vector/bar/arc mover rules and tests Introduce three new mover rules in Rules/Braille/Russian/Russian_Rules.yaml: vector-arrow, vector-bar, and geometry-arc. Each rule detects specific accent characters (arrows: → ⟶ ⇀ ⃗; bars: ¯ ‾ _  ̄; arcs: ⌒ ⏜ ︵) and emits the appropriate braille indicators, using a different form when the base has more than one character. Add corresponding tests in tests/braille/Russian/russian.rs to cover single- and multi-character movers and combined expressions. * Add Russian braille math functions and limits Extend Russian braille rules to support additional math functions and large operator limits. Added new rules for large-op-limits, operator/functions (including special handling for div), and div-specific replacements in Rules/Braille/Russian/*. Updated Russian definitions and unicode mappings to include sinh/cosh/tanh/coth, arg/sgn/det/rank/rg, Re/Im, grad/rot/div, gcd/lcm and related braille codes. Updated global Rules/definitions.yaml to recognize new function names. Fixed braille cleanup in src/braille.rs to normalize div encoding. Added unit tests exercising the new function mappings and large operator limits. * Support system left brace in Russian braille Introduce handling for system left-brace matrices in the Russian braille rules: add InSystemLeftBrace variable, a system-left-brace-opener rule and a system-left-brace-table rule to detect brace-enclosed mtable as a system. Update row-start logic to emit appropriate braille tokens when InSystemLeftBrace is set. Add tests exercising curly-brace matrices, piecewise function (brace with conditions), and vertical-bar matrices to verify the new behavior. * Add Russian chemistry braille rules and tests Extend Russian braille rules to handle chemical notation: add rules for equilibrium-arrow, reaction operators, repeated charges, charges, and charge-with-subscript mappings (chemical-reaction-operator, chemistry-mhchem-equilibrium-arrow, chemistry-repeated-charge, chemistry-charge, chemistry-charge-with-subscript). Update Rules/Braille/Russian/Russian_Rules.yaml to map arrows, +/-, superscripts/subscripts and reaction operators to appropriate braille sequences. Add tests in tests/braille/Russian/russian.rs (source_chemical_reactions_and_charges) covering reaction arrows, charges, subscripts/superscripts and combined examples. --------- Co-authored-by: Moritz Groß Co-authored-by: nsoiffer Co-authored-by: Tuomas Pyorre <82783219+ttpyorre@users.noreply.github.com> Co-authored-by: V3nky Co-authored-by: Hon-Jang Yang Co-authored-by: nsoiffer Co-authored-by: Cursor * Add Russian braille rules for geometry operators Add three new braille encoding rules for geometry operators (∠, △, ∪) in Russian: - canonical-prefix-geometry-op: handles geometry operators in canonical form - geometry-prefix-after-canonical-multiplier: handles operators after canonical multipliers - geometry-prefix-after-multiplier: handles operators after regular multipliers Includes corresponding test cases for angles, triangles, unions, perpendicular, and similar symbols. * Improve Russian braille typeform and mathvariant handling Refactor Russian braille cleanup to properly handle typeform indicators (bold, italic) and mathvariant attributes. Introduces a dedicated typeform indicator function that manages indicator placement according to Russian Braille ГОСТ Р 58511 standard, separating typeform handling from alphabet indicators. Adds support for automatic typeform indicators based on mathvariant attributes, ensuring proper indicator pairing around content blocks. Updates unicode-full.yaml rules to reflect new typeform codes and adds comprehensive tests for various mathvariant combinations. * Propagate mathvariant attribute to children Add mathvariant inheritance from parent elements to children that lack their own mathvariant attribute. This ensures consistent styling throughout nested elements. Includes tests for bold text propagation through mstyle and mrow containers, and verifies that explicit child mathvariants override inherited values. * Fix clippy warnings and improve code style - Remove unnecessary borrows in format! and display calls - Change string slices to arrays in trim_end_matches calls - Replace expect(&format!(...)) with unwrap_or_else(|_| panic!(...)) - Add clippy allow attributes to suppress false positives in tests - Add missing newline at end of build.rs * Remove unnecessary borrows in format macros Remove redundant `&` reference operators from arguments passed to format! macros and similar functions. These values are automatically borrowed by the format machinery, so explicit borrows are unnecessary and reduce readability. * Normalize Unicode math digits in decimal numbers Add normalization of Unicode mathematical alphanumeric symbols (Plane 1 digits) to ASCII digits for number nodes containing decimal points. Skip canonicalize_plane1 processing for already-normalized nodes to prevent double processing. * Remove unnecessary references and .to_string() calls Remove unnecessary `&` borrows in format arguments and replace `.to_string()` with direct display. Simplify byte array literals to byte string literals for readability and idiomatic Rust style. * Remove mathvariant propagation and update tests Drop implicit propagation of the mathvariant attribute from canonicalization (removed propagate_inherited_mathvariant call and helper functions) and update Russian braille tests to set mathvariant explicitly on elements (replace mstyle with mrow and move attributes to mi). Changes in src/canonicalize.rs and tests/braille/Russian/russian.rs. This makes mathvariant handling explicit in tests and avoids mutating child nodes during canonicalization. * Remove math digit normalization for mn nodes Delete the conditional normalization of mathematical digit characters for text and remove the normalize_math_digits helper from src/canonicalize.rs. Previously the code converted several Unicode math-digit codepoints to ASCII when a decimal point was present. * Adjust test expectations and unignore tests Update expected spoken/braille test outputs across multiple language test files to match updated speech rules (punctuation handling, table phrasing, and unit/roman-numeral rendering). Removed several #[ignore] annotations for the previously skipped "ignore_period_and_space" tests. Minor tidy in pl/definitions.rs to avoid unnecessary format! usage and fix EOF newline. Affected files include tests/Languages/* for en, fi, fr, hu, nb, pl, sv, vi, and zh/tw. * Bump to 0.7.6-beta.8 and update dependencies Update to version 0.7.6-beta.8 with the following changes: - Migrate to no-unsafe variants of sxd-document and sxd-xpath - Add 'no-unsafe' feature flag for unsafe-free builds - Update dependencies: strum, regex, bitflags, env_logger - Add tempfile dev-dependency - Improve build.rs: add rerun-if-changed directives earlier and fix indentation - Exclude BrailleDocs from package * Switch to sxd_document_no_unsafe and NameStr Replace sxd_document/sxd_xpath usages with no-unsafe variants and adapt string types. Key changes in src/braille.rs, src/canonicalize.rs, and src/chemistry.rs: introduce NameStr (feature-gated), use as_str!/as_qname! helpers, convert many attribute_value calls to .as_deref(), update XPath error constructions, and store operator chars as NameStr with PhantomData in OperatorPair. Refactor chemistry tests to helper-based Result flow and use sxd_document_no_unsafe::parser. These edits make the code compatible with the "no-unsafe" pathway and fix lifetime/string mismatches across the codebase. * Use no-unsafe sxd crates; adapt code & tests Replace sxd_document/sxd_xpath with sxd_document_no_unsafe and sxd_xpath_no_unsafe and update imports (including parser). Adjust attribute handling and lifetimes: use as_str!, attribute_value(...).as_deref().unwrap_or_default(), and similar changes to avoid unsafe string lifetimes. Refactor tests to return Result, use ? and panic-catching, and update assertions (e.g. is_empty). Re-enable sample MathML in main, tweak timing_test (disable heavy loops, change loop count). Misc small fixes to satisfy the new APIs and lifetime semantics. * Use no-unsafe sxd libs; refactor TTS & parsing Switch project XML/XPath handling to sxd_document_no_unsafe / sxd_xpath_no_unsafe and adapt code to use as_str!/as_qname! helpers. Update speech and pretty-print logic to use the new context and node APIs, tighten unicode handling, and add debug checks. Refactor TTS APIs so command value accessors return Result and TTS string builders propagate errors. Update tests and prefs helpers (temporary rules dir helper, prefs test flow). Misc: small capacity tweaks and wording fixes in error messages. Files changed: src/prefs.rs, src/pretty_print.rs, src/speech.rs, src/tts.rs. * Improve wording in RU user guide Replace "Трудности в обучении" with "Когнитивные нарушения" in docs/ru/User_guide_for_MathCAT_ru.md to use a more precise and respectful term for users who need shorter spoken output. * Add Russian GOST-58511 typeform braille rules and tests Introduce three Russian GOST-58511 typeform/layout braille rules (small-font, letter-spacing, insertion) to Rules/Braille/Russian/Russian_Rules.yaml with their corresponding braille markers. Expand and reorganize tests in tests/braille/Russian/russian.rs: add coverage for the new typeform indicators, interaction cases, menclose/table/braces, and other edge cases; rename several tests to reflect sectioned/aggregated variants. Also adjust expected braille outputs for a few existing tests (sqrt and geometry matrix cases) to match the new rules/formatting. --------- Co-authored-by: nsoiffer Co-authored-by: mgros Co-authored-by: Tuomas Pyorre <82783219+ttpyorre@users.noreply.github.com> Co-authored-by: V3nky Co-authored-by: Hon-Jang Yang Co-authored-by: nsoiffer Co-authored-by: Cursor * force bring over the files from main -- merging didn't do the right thing * Ru - handle lone log/ln; suppress nav announcements (#681) * ru: handle lone log/ln; suppress nav announcements Add handling for lone mi elements 'log' and 'ln' in ClearSpeak (supports Verbosity and ClearSpeak_Log LnAsNaturalLog option). Add a navigation rule to avoid announcing "in/out of" for prefix or silent intents that have no NavigationParts. Include unit tests: new ClearSpeak tests for individual log/ln, new navigate tests for suppressed base announcements, and register the navigate test module. * Russian braille: bracket/arrow mappings & tests Add contextual bracket-recognition rules (square and angle) to Rules/Braille/Russian/Russian_Rules.yaml and add Unicode braille mappings for ≶, ≷, ⟨, ⟩, ↑, ↓ in Rules/Braille/Russian/unicode.yaml. Extend tests in tests/braille/Russian/russian.rs to cover Russian Braille GOST 2019 order relations, large vertical arrows, and contextual bracket recognition for square and angle brackets, validating the expected braille output. --------- Co-authored-by: Danil <81031453+Kostenkov-2021@users.noreply.github.com> Co-authored-by: mgros Co-authored-by: Tuomas Pyorre <82783219+ttpyorre@users.noreply.github.com> Co-authored-by: V3nky Co-authored-by: Hon-Jang Yang Co-authored-by: nsoiffer Co-authored-by: Cursor --- Rules/Braille/Russian/Russian_Rules.yaml | 26 +++ Rules/Braille/Russian/unicode.yaml | 6 + Rules/Braille/UEB/unicode-full.yaml | 35 ++- Rules/Languages/de/unicode-full.yaml | 225 ++++++++++++++++++++ Rules/Languages/en/SharedRules/default.yaml | 29 +-- Rules/Languages/en/unicode-full.yaml | 185 +++++++++++++++- Rules/Languages/ru/ClearSpeak_Rules.yaml | 18 ++ Rules/Languages/ru/navigate.yaml | 9 + Rules/Languages/zh/tw/unicode-full.yaml | 1 + build.rs | 8 +- tests/Languages/en/mtable.rs | 8 +- tests/Languages/en/shared.rs | 11 +- tests/Languages/fi/shared.rs | 3 +- tests/Languages/fr/shared.rs | 3 +- tests/Languages/hu/shared.rs | 3 +- tests/Languages/nb/shared.rs | 3 +- tests/Languages/pl/definitions.rs | 8 +- tests/Languages/pl/shared.rs | 3 +- tests/Languages/ru.rs | 3 +- tests/Languages/ru/ClearSpeak/functions.rs | 16 ++ tests/Languages/ru/navigate.rs | 56 +++++ tests/Languages/sv/shared.rs | 3 +- tests/Languages/vi/vi.rs | 8 +- tests/Languages/zh/tw/shared.rs | 3 +- tests/braille/Russian/russian.rs | 29 +++ 25 files changed, 615 insertions(+), 87 deletions(-) create mode 100644 tests/Languages/ru/navigate.rs diff --git a/Rules/Braille/Russian/Russian_Rules.yaml b/Rules/Braille/Russian/Russian_Rules.yaml index ee9004aed..1bf1603c9 100644 --- a/Rules/Braille/Russian/Russian_Rules.yaml +++ b/Rules/Braille/Russian/Russian_Rules.yaml @@ -1555,6 +1555,32 @@ replace: - x: "DefinitionValue(., 'Braille', 'RussianFunctionBraille')" +- + name: contextual-square-bracket-recognition-point + tag: mo + match: + - "parent::*[IsBracketed(., '[', ']')] and" + - "((.='[' and not(preceding-sibling::*) and following-sibling::*[1][self::m:mi or self::m:mtext]) or" + - " (.=']' and not(following-sibling::*) and preceding-sibling::*[1][self::m:mi or self::m:mtext]))" + replace: + - T: "⠠" + - x: "text()" + +- + name: contextual-angle-bracket-recognition-point + tag: mo + match: + - "parent::*[IsBracketed(., '⟨', '⟩')] and" + - "((.='⟨' and not(preceding-sibling::*)) or (.='⟩' and not(following-sibling::*))) and" + - "(parent::*/*[2][self::m:mi or self::m:mtext] or" + - " parent::*/*[last()-1][self::m:mi or self::m:mtext])" + replace: + - T: "⠈" + - test: + - if: ".='⟨'" + then: [T: "⠪"] + else: [T: "⠕"] + - name: div-operator-function tag: mo diff --git a/Rules/Braille/Russian/unicode.yaml b/Rules/Braille/Russian/unicode.yaml index 6b08895b9..4fc43ea9d 100644 --- a/Rules/Braille/Russian/unicode.yaml +++ b/Rules/Braille/Russian/unicode.yaml @@ -214,6 +214,8 @@ - "≢": [t: "⠀⠰⠾"] - "≤": [t: "⠀⠪⠶"] - "≥": [t: "⠀⠕⠶"] +- "≶": [T: "⠀⠪⠕⠀"] +- "≷": [T: "⠀⠕⠪⠀"] - "≪": [t: "⠀⠪⠪⠀"] - "≫": [t: "⠀⠕⠕⠀"] - "≺": [t: "⠀⠒⠪⠀"] @@ -238,6 +240,10 @@ - "⋰": [t: "⠠⠲⠲⠲"] - "⋱": [t: "⠠⠲⠲⠲"] - "△": [t: "⠸⠙"] +- "⟨": [T: "⠪"] +- "⟩": [T: "⠕"] +- "↑": [T: "⠰⠌"] +- "↓": [T: "⠘⠡"] - "→": [t: "⠀⠒⠕"] - "⟶": [t: "⠀⠒⠕"] - "←": [t: "⠀⠦⠶"] diff --git a/Rules/Braille/UEB/unicode-full.yaml b/Rules/Braille/UEB/unicode-full.yaml index 9114b4de6..277eb9f4a 100644 --- a/Rules/Braille/UEB/unicode-full.yaml +++ b/Rules/Braille/UEB/unicode-full.yaml @@ -1,24 +1,6 @@ --- - - - " ": [t: "W"] # 0x2000 - - " ": [t: "W"] # 0x2001 - - " ": [t: "W"] # 0x2002 - - " ": [t: "W"] # 0x2003 - - " ": [t: "W"] # 0x2004 - - " ": [t: "W"] # 0x2005 - - " ": [t: "W"] # 0x2006 - - " ": [t: "W"] # 0x2007 - - " ": [t: "W"] # 0x2008 - - " ": [t: "W"] # 0x2009 - - " ": [t: "W"] # 0x200a - - "​": [t: "W"] # 0x200b - - "
": [t: "W"] # 0x2028 - - "
": [t: "W"] # 0x2029 - - " ": [t: "W"] # 0x202f - - " ": [t: "W"] # 0x205f - - "": [t: "W"] # 0xfeff - - + - "⋇": [tc: "1⠌⠯⠦"] # 0x22C7 (Division times) + - "⊩": [tc: "⠸⠳⠿⠸⠒"] # 0x22A9 (Forces) - "ℏ": [tc: "⠈⠒⠓"] # t: "B"F (reduced Plank's constant) - "ⅆ": [tc: "⠙"] # 0x2146 - "ⅇ": [tc: "⠑"] # 0x2147 @@ -189,6 +171,7 @@ - spell: "translate('.', '₊₋₌₍₎', '⠖⠤⠶⠣⠜')" - "™": [t: "⠘⠞"] # 0x2122 + - "ℊ": [tc: "TL⠛⠁"] # 0x210a (Script Small G) - "ℋ": [tc: "TCL⠓⠁"] # 0x210b (Script Capital H) - "ℒ": [tc: "TCL⠇"] # 0x2113 (Script Capital L) @@ -215,6 +198,16 @@ - "ℤ": [tc: "𝔹CL⠵"] # 0x2124 + - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 + - tc: "B" + - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒𝛓𝛔𝛕𝛖𝛗𝛘𝛙𝛚', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + + - "𝛂-𝛚": # 0x1d6c2 - 0x1d6da + - tc: "BGL" + - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒𝛓𝛔𝛕𝛖𝛗𝛘𝛙𝛚', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "𝔄-𝔜": # 0x1d504 - 0x1d51d ('z' version is reserved) - tc: "DC" - spell: "translate('.', '𝔄𝔅𝔆𝔇𝔈𝔉𝔊𝔋𝔌𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔕𝔖𝔗𝔘𝔙𝔚𝔛𝔜', 'abcdefghijklmnopqrstuvwxyz')" @@ -248,7 +241,7 @@ # script chars in math alphabetic block and also MathType private use area - "𝒜-𝒵": # 0x1d49c - 0x1d4b5 - tc: "TsC" - - spell: "translate('.', '𝒜𝒝𝒞𝒟𝒠𝒡𝒢𝒣𝒤𝒥𝒦𝒧𝒨𝒩𝒪𝒫𝒬𝒭𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'abcdefghijklmnopqrstuvwxyz')" + - spell: "translate('.', '𝒜𝒝𝒞𝒟𝒠𝒡𝒢𝒣𝒤𝒥𝒦𝒧𝒨𝒩𝒪𝒫𝒬𝒭𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'abcdefghijklmnopqrstuvwxyza')" - "𝒶-𝓏": # 0x1d4b6 - 0x1d4cf - tc: "Ts" diff --git a/Rules/Languages/de/unicode-full.yaml b/Rules/Languages/de/unicode-full.yaml index 03b3157a6..8a98107f8 100644 --- a/Rules/Languages/de/unicode-full.yaml +++ b/Rules/Languages/de/unicode-full.yaml @@ -10,6 +10,7 @@ - "¨": [T: "diaeresis"] # 0xa8 (en: 'double dot') - "©": [T: "Urheberrecht"] # 0xa9 (en: 'copyright') - "ª": [T: "weibliches Ordnungszeichen"] # 0xaa (en: 'feminine ordinal indicator') + - "¬": [T: "nicht"] # 0xac (en: 'not') - "«": [T: "Nach links zeigendes doppeltes spitzes Anführungszeichen"] # 0xab (en: 'left-pointing double angle quote mark', MathPlayer: 'left-pointing double angle quote mark', google: 'links zeigende doppelwinkel-zitatmarke') - "¯": # 0xaf - test: @@ -18,9 +19,24 @@ else: [T: "Überstrich"] # (en: 'line', MathPlayer: 'macron', google: 'linie') - "²": [T: "Quadrat"] # 0xb2 (en: 'two', MathPlayer: 'zum Quadrat', google: 'zwei') - "³": [T: "Kubik"] # 0xb3 (en: 'three', MathPlayer: 'hoch drei', google: 'drei') + - "´": [T: "Akutakzent"] # 0xb4 (en: 'acute', MathPlayer: 'acute accent', google: 'akut') - "µ": [T: "Mikrozeichen"] # 0xb5 (en: 'micro', MathPlayer: 'micro sign', google: 'mikro') - "¹": [T: "hoch eins"] # 0xb9 (en: 'one', google: 'eins') - "º": [T: "Männliches Ordnungszeichen"] # 0xb9 (en: 'masculine ordinal indicator') + - "·": + - test: + if: "$SpeechStyle != 'ClearSpeak' or $ClearSpeak_MultSymbolDot = 'Auto'" + then: [T: "mal"] # (en: 'times', google translation) + else: [T: "mal"] # (en: 'dot', MathPlayer: 'Punkt mittig', google: 'punkt') + - "×": # 0xd7 + - test: + if: "$SpeechStyle != 'ClearSpeak' or $ClearSpeak_MultSymbolX = 'Auto'" + then: [T: "mal"] # (en: 'times', google translation) + else_test: + if: $ClearSpeak_MultSymbolX = 'By' + then: [T: "von"] # (en: 'by', google translation) + else: [T: "mal"] # (en: 'cross', MathPlayer: 'times', google: 'kreuzen') + - "÷": [T: "geteilt durch"] # 0xf7 (en: 'divided by', MathPlayer: 'divides') - "¡": [T: "umgekehrtes ausrufezeichen"] # 0xa1 (en: 'inverted exclamation mark') - "¶": [T: "absatzzeichen"] # 0xb6 (en: 'paragraph mark') - "¿": [T: "umgekehrtes fragezeichen"] # 0xbf (en: 'inverted question mark') @@ -108,6 +124,14 @@ - "˽": [T: "modifikatorregal"] # 0x2fd (en: 'modifier shelf', google translation) - "˾": [T: "modifikator offenes regal"] # 0x2fe (en: 'modifier open shelf', google translation) - "˿": [T: "modifikator niedriger links pfeil"] # 0x2ff (en: 'modifier low left arrow', google translation) + - "̀": [T: "gravisakzentverzierung"] # 0x300 (en: 'grave accent embellishment', google: 'grabakzentverzierung') + - "́": [T: "akutakzentverzierung"] # 0x301 (en: 'acute accent embellishment', google: 'akute akzentverzierung') + - "̂": [T: "Hutverzierung"] # 0x302 (en: 'circumflex accent embellishment', google: 'zirma akzentverzierung') + - "̃": [T: "tildeverzierung"] # 0x303 (google: 'tilde verzierung') + - "̄": [T: "makronverzierung"] # 0x304 (en: 'macron embellishment', google: 'makron -verzierung') + - "̅": [T: "Überstreifenverzierung"] # 0x305 (google: 'überbärerverzierung') + - "̆": [T: "kombinierendes Breve"] # 0x306 (en: 'breve embellishment', google: 'breve -verzierung') + - "̇": [T: "Punkt über Verzierung"] # 0x307 (google: 'punkt über verzierung') - "̈": [T: "zwei Punkte oberhalb"] # 0x308 (en: 'diaeresis embellishment', google: 'diaeresis -verzierung') - "̉": [T: "haken über verzierung"] # 0x309 (en: 'hook above embellishment', google translation) - "̊": [T: "ring über verzierung"] # 0x30a (en: 'ring above embellishment', google: 'ring über verzierung') @@ -197,9 +221,13 @@ - "ϒ": [T: "Variante Upsilon mit Haken"] # 0x3d2 (en: 'upsilon with hook', google: 'upsilon mit haken') - "ϓ": [T: "Upsilon mit Akutakzent und Haken"] # 0x3d3 (en: 'upsilon with acute and hook', google translation) - "ϔ": [T: "Upsilon mit Diaeresis und Haken"] # 0x3d4 (en: 'upsilon with diaeresis and hook', google translation) + - "ϕ": [T: "gerades Phi"] # 0x3d5 (en: 'phi', google: 'phi') + - "ϖ": [T: "Variante Pi"] # 0x3d6 (en: 'pi', google: 'pi') - "ϗ": [T: "kai"] # 0x3d7 (MathPlayer: 'variant kai') - "Ϙ": [T: "großes archaisches koppa"] # 0x3d8 (en: 'cap archaic koppa') - "ϙ": [T: "archaisches koppa"] # 0x3d9 (en: 'archaic koppa') + - "ϵ": [T: "epsilon"] # 0x3f5 + - "϶": [T: "umgekehrtes epsilon"] # 0x3f6 (en: 'reversed epsilon', MathPlayer: 'reversed epsilon', google: 'umgekehrter epsilon') - "А-Я": # 0x410 - 0x42f - test: if: "$CapitalLetters_Beep" @@ -256,6 +284,10 @@ - "‐": [T: "Bindestrich"] # 0x2010 (en: 'hyphen', MathPlayer: 'minus', google: 'bindestrich') - "‑": [T: "nicht-trennender Bindestrich"] # 0x2011 (en: 'hyphen', google: 'bindestrich') - "‒": [T: "Figurenstrich"] # 0x2012 (en: 'figure dash', google: 'figur dash') + - "–": [T: "Halbgeviertstrich"] # 0x2013 (SRE: 'En Dash') + - "—": [T: "Geviertstrich"] # 0x2014 (SRE: 'EM Dash') + - "―": [T: "Horizontale Linie"] # 0x2015 (en: 'horizontal bar', MathPlayer: 'horizontal bar', google: 'horizontale linie') + - "‖": [T: "Doppelte vertikale Linie"] # 0x2016 (en: 'double vertical line', MathPlayer: 'double vertical bar', google: 'doppelte vertikale linie') - "†": [T: "Dolch"] # 0x2020 (en: 'dagger', MathPlayer: 'dagger', google: 'dolch') - "‡": [T: "Doppelter Dolch"] # 0x2021 (en: 'double dagger', MathPlayer: 'double dagger', google: 'doppeldolch') @@ -267,8 +299,24 @@ then: [T: "punkt"] # (en: 'dot', google translation) else: [T: "Kugel"] # (en: 'bullet', MathPlayer: 'bullet', google: 'kugel') + - "…": # 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][text()='…']" + then: [T: "punkt punkt punkt"] # (en: 'dot dot dot', google translation) + else_test: # must have $ClearSpeak_Ellipses = 'AndSoOn' + if: "count(following-sibling::*) = 0" + then: [T: "und so weiter"] # (en: 'and so on', google translation) + else: [T: "horizontale Ellipse"] # (en: 'and so on up to', MathPlayer: 'dot dot dot', google: 'und so weiter bis') + - "‰": [T: "promille"] # 0x2030 (en: 'per mille', MathPlayer: 'per mille sign', google: 'pro mille') - "‱": [T: "pro Zehntausend"] # 0x2031 (en: 'per ten thousand', MathPlayer: 'per ten thousand sign', google: 'pro zehntausend') + - "′": [T: "Strich"] # 0x2032 (en: 'prime', MathPlayer: 'strich', google: 'prime') + - "″": [T: "zwei Striche"] # 0x2033 (en: 'double prime', MathPlayer: 'zwei-strich', google: 'double prime') + - "‴": [T: "drei Striche"] # 0x2034 (en: 'triple prime', MathPlayer: 'drei-strich', google: 'triple prime') - "‵": [T: "umgekehrter Strich"] # 0x2035 (en: 'reversed prime', MathPlayer: 'reversed prime', google: 'umgekehrt prime') - "‶": [T: "zwei umgekehrte Striche"] # 0x2036 (en: 'reversed double prime', MathPlayer: 'reversed double prime', google: 'umgekehrt doppelte prime') - "‷": [T: "drei umgekehrte Striche"] # 0x2037 (en: 'reversed triple prime', google: 'triple prime umgekehrt') @@ -398,6 +446,7 @@ - "℆": [T: "cada una"] # 0x2106 (google translation) - "ℇ": [T: "Euler-Konstante"] # 0x2107 (en: 'euler's constant', google: 'eulers konstante') - "℈": [T: "skrupel"] # 0x2108 (en: 'scruples', google translation) + - "℉": [T: "Grad Fahrenheit"] # 0x2109 (google: 'grad fahrenheit') - "ℊ": [T: "Skript G"] # 0x210a (google: 'skript g') - "ℌℑℨℭ": # 0x210c, 0x2111, 0x2128, 0x212d - T: "Fraktur" # (en: 'fraktur', google: 'fraktur') @@ -409,6 +458,7 @@ then: [T: "doppelt geschlagen"] # (en: 'double struck', google translation) - spell: "translate('.', 'ℍℙℾℿ', 'HPΓΠ')" + - "ℎ": [T: "Planck-Konstante"] # 0x210e (en: 'planck constant', MathPlayer: 'planck constant', google: 'planck konstant') - "ℏ": # 0x210f - test: if: "($Verbosity='Terse')" @@ -419,11 +469,15 @@ - T: "Skript" # (en: 'script', MathPlayer: 'h bar', google: 'skript') - spell: "translate('.', 'ℐℒ℘ℬℰℱℳ', 'ILPBEFM')" + - "ℓ": [T: "Schreibschrift l"] # 0x2113 (en: 'script l', MathPlayer: 'script l', google: 'skript l') - "℔": [T: "L B Balkensymbol"] # 0x2114 (en: 'pounds', google: 'pfund') - "№": [T: "Numero-Zeichen"] # 0x2116 (en: 'number', MathPlayer: 'numero sign', google: 'nummer') - "℥": [T: "Unze-Zeichen"] # 0x2125 (en: 'ounces', MathPlayer: 'ounce sign', google: 'unzen') + - "Ω": [T: "Ohm-Zeichen"] # 0x2126 (en: 'ohms', MathPlayer: 'ohm sign', google: 'ohm') - "℧": [T: "Umgekehrtes Ohm-Zeichen"] # 0x2127 (en: 'mhos', MathPlayer: 'inverted ohm', google: 'mhos') - "℩": [T: "gedrehtes Iota"] # 0x2129 (google: 'drehte iota') + - "K": [T: "Kelvin-Zeichen"] # 0x212a (en: 'kelvin', MathPlayer: 'degrees kelvin', google: 'kelvin') + - "Å": [T: "Angstrom-Zeichen"] # 0x212b (en: 'angstroms', MathPlayer: 'angstroms', google: 'angstrome') - "ℯ": [T: "Skript E"] # 0x212f (google: 'skript e') # coalesced some chars that use cap letters @@ -501,6 +555,10 @@ - "ⅾ": [T: "ⅾ"] # 0x217e (en: 'D', google translation) - "ⅿ": [T: "ⅿ"] # 0x217f (en: 'M', google translation) - "↉": [T: "null drittel"] # 0x2189 (en: 'zero thirds') + - "←": [T: "Pfeil nach links"] # 0x2190 (en: 'leftwards arrow', MathPlayer: 'leftwards arrow', google: 'pfeil nach links') + - "↑": [T: "Aufwärtspfeil"] # 0x2191 (en: 'upwards arrow', MathPlayer: 'upwards arrow', google: 'nach oben pfeil') + - "→": [T: "Pfeil nach rechts"] # 0x2192 (en: 'rightwards arrow', MathPlayer: 'rightwards arrow', google: 'richtiger pfeil') + - "↓": [T: "Pfeil nach unten"] # 0x2193 (en: 'downwards arrow', MathPlayer: 'downwards arrow', google: 'nach unten pfeil') - "↔": [T: "Pfeil nach links und rechts"] # 0x2194 (en: 'left right arrow', MathPlayer: 'left right arrow', google: 'links rechts pfeil') - "↕": [T: "Pfeil nach oben und unten"] # 0x2195 (en: 'up down arrow', MathPlayer: 'up down arrow', google: 'auf pfeil runter') - "↖": [T: "Nordwestpfeil"] # 0x2196 (en: 'north west arrow', MathPlayer: 'north west arrow', google: 'nordwestpfeil') @@ -573,6 +631,7 @@ - "⇏": [T: "Doppelpfeil nach rechts mit Strich"] # 0x21cf (en: 'rightwards double arrow with stroke', MathPlayer: 'rightwards double arrow with stroke', google: 'richtiger doppelpfeil mit schlaganfall') - "⇐": [T: "Doppelpfeil nach links"] # 0x21d0 (en: 'leftwards double arrow', MathPlayer: 'leftwards double arrow', google: 'links doppelpfeil') - "⇑": [T: "Doppelpfeil nach oben"] # 0x21d1 (en: 'upwards double arrow', MathPlayer: 'upwards double arrow', google: 'double -pfeil nach oben') + - "⇒": [T: "Doppelpfeil nach rechts"] # 0x21d2 (en: 'rightwards double arrow', MathPlayer: 'rightwards double arrow', google: 'richtiger doppelpfeil') - "⇓": [T: "Doppelpfeil nach unten"] # 0x21d3 (en: 'downwards double arrow', MathPlayer: 'downwards double arrow', google: 'downwards doppelpfeil') - "⇔": [T: "Linker rechter Doppelpfeil"] # 0x21d4 (en: 'left right double arrow', MathPlayer: 'left right double arrow', google: 'links rechts doppelpfeil') - "⇕": [T: "Doppelpfeil nach oben und unten"] # 0x21d5 (en: 'up down double arrow', MathPlayer: 'up down double arrow', google: 'doppelpfeil nach oben') @@ -624,18 +683,107 @@ if: "$Verbosity!='Terse'" then: [T: "die"] # (en: 'the', google translation) - T: "Komplement" # (en: 'complement of') + - "∂": # 0x2202 + - test: + if: "$Verbosity='Terse'" + then: [T: "partiell"] # (en: 'partial', google translation) + else: [T: "partielle Ableitung"] # (en: 'partial derivative', google: 'partielle ableitung') - "∃": [T: "es gibt"] # 0x2203 (en: 'there exists') - "∄": [T: "es gibt nicht"] # 0x2204 (en: 'there does not exist') - "∅": [T: "leere Menge"] # 0x2205 (en: 'empty set') + - "∆": # 0x2206 + - test: + if: "$Verbosity!='Terse'" + then: [T: "die"] # (en: 'the', google translation) + - T: "Laplace-Operator von" # (en: 'laplacian of', MathPlayer: 'increment', google: 'laplace von') + - "∇": # 0x2207 + - test: + if: "$Verbosity!='Terse'" + then: [T: "die"] # (en: 'the', google translation) + - T: "Nabla" # (en: 'gradient of', google: 'gradient von') + - "∈": # 0x2208 + - test: + if: "$SpeechStyle != 'ClearSpeak'" + then: [T: "ein Element von"] # (en: 'an element of', google translation) + # 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: "in"] # (google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "Element von"] # (en: 'member of', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "Element von"] # (en: 'element of', google translation) + - else: [T: "Element von"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'belonging to') + else_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "ist ein Element von"] # (en: 'is a member of', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "ist ein Element von"] # (en: 'is an element of', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "ist in"] # (en: 'is in', google translation) + - else: [T: "Element von"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'belongs to') + - "∉": # 0x2209 + # rule is identical to 0x2208 + - test: + if: "$SpeechStyle != 'ClearSpeak'" + then: [T: "ist kein Element von"] # (en: 'is not an element of', google translation) + # 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: "nicht in"] # (en: 'not in', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "kein Element von"] # (en: 'not member of', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "kein Element von"] # (en: 'not element of', google translation) + - else: [T: "nicht Element von"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'not belonging to') + else_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "ist kein Element von"] # (en: 'is not a member of', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "ist kein Element von"] # (en: 'is not an element of', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "ist nicht in"] # (en: 'is not in', google translation) + - else: [T: "nicht Element von"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'does not belong to') + - "∊": # 0x220a + - test: + if: "$SpeechStyle != 'ClearSpeak'" + then: [T: "ist ein Element von"] # (en: 'is an element of', google translation) + # 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: "in"] # (google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "Element von"] # (en: 'member of', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "Element von"] # (en: 'element of', google translation) + - else: [T: "Element von"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'belonging to') + else_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "ist ein Element von"] # (en: 'is a member of', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "ist ein Element von"] # (en: 'is an element of', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "ist in"] # (en: 'is in', google translation) + - else: [T: "Element von"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'belongs to') - "∋": [T: "hat als Element"] # 0x220b (en: 'contains the member') - "∌": [T: "hat nicht als Element"] # 0x220c (en: 'does not contain the member') - "∍": [T: "hat als Element"] # 0x220d (en: 'contains the member') - "∎": [T: "Ende Beweis"] # 0x220e (en: 'end of proof') + - "∏": [T: "Produkt"] # 0x220f (en: 'product', MathPlayer: 'Product', google: 'produkt') + - "∐": [T: "Koprodukt"] # 0x2210 (en: 'coproduct', MathPlayer: 'coproduct', google: 'koprodukt') + - "∑": [T: "Summe"] # 0x2211 (en: 'sum') - "−": [T: "minus"] # 0x2212 - "∓": [T: "minus plus"] # 0x2213 (en: 'minus or plus') - "∔": [T: "Plus mit Punkt"] # 0x2214 (en: 'dot plus', MathPlayer: 'dot plus', google: 'punkt plus') - "∕": [T: "Division"] # 0x2215 (en: 'divided by', MathPlayer: 'Schrägstrich (Division)', google: 'geteilt durch') - "∖": [T: "Mengendifferenz"] # 0x2216 (en: 'set minus', MathPlayer: 'Mengen-Minus', google: 'minus einstellen') + - "∗": [T: "Sternoperator"] # 0x2217 (en: 'times', MathPlayer: 'asterisk operator', google: 'mal') - "∘": [T: "verknüpft mit"] # 0x2218 (en: 'composed with') - "∙": # 0x2219 - test: @@ -643,6 +791,11 @@ then: [T: "punkt"] # (en: 'dot', google translation) else: [T: "Kreisoperator"] # (en: 'times', MathPlayer: 'bullet operator', google: 'mal') + - "√": # 0x221a + - test: + if: "$Verbosity!='Terse'" + then: [T: "die"] # (en: 'the', google translation) + - T: "Quadratwurzel" # (en: 'square root of', MathPlayer: 'radical', google: 'quadratwurzel von') - "∛": # 0x221b - test: if: "$Verbosity!='Terse'" @@ -653,12 +806,26 @@ if: "$Verbosity!='Terse'" then: [T: "die"] # (en: 'the', google translation) - T: "vierte Wurzel" # (en: 'fourth root of') + - "∝": # 0x221d + - test: + if: "$Verbosity!='Terse'" + then: [T: "ist"] # (en: 'is', google translation) + - T: "proportional zu" # (en: 'proportional to') - "∞": [T: "unendlich"] # 0x221e (en: 'infinity') + - "∟": [T: "rechter Winkel"] # 0x221f (en: 'right angle') + - "∠": [T: "Winkel"] # 0x2220 (en: 'angle') + - "∡": [T: "gerichteter Winkel"] # 0x2221 (en: 'measured angle', MathPlayer: 'measured angle', google: 'gemessener winkel') - "∢": [T: "Raumwinkel"] # 0x2222 (en: 'spherical angle', MathPlayer: 'spherical angle', google: 'sphärischer winkel') - "∣": [T: "teilt"] # 0x2223 (en: 'divides') - "∤": [T: "teilt nicht"] # 0x2224 (en: 'does not divide') - "∧": [T: "und"] # 0x2227 (en: 'and') - "∨": [T: "oder"] # 0x2228 (en: 'or') + - "∩": [T: "Durchschnitt"] # 0x2229 (en: 'intersection') + - "∪": [T: "Vereinigung"] # 0x222a (en: 'union') + - "∫": [T: "Integral"] # 0x222b (en: 'integral') + - "∬": [T: "Doppelintegral"] # 0x222c (en: 'double integral') + - "∭": [T: "Dreifachintegral"] # 0x222d (en: 'triple integral', MathPlayer: 'Dreifaches Integral', google: 'dreifachintegral') + - "∮": [T: "Randintegral"] # 0x222e (en: 'contour integral', MathPlayer: 'Kontur-Integral', google: 'konturintegral') - "∯": [T: "Oberflächenintegral"] # 0x222f (en: 'surface integral', MathPlayer: 'Flächenintegral', google: 'oberflächenintegral') - "∰": [T: "Volumenintegral"] # 0x2230 (en: 'volume integral', MathPlayer: 'Volumsintegral', google: 'volumenintegral') - "∱": [T: "Integral im Uhrzeigersinn"] # 0x2231 (en: 'clockwise integral') @@ -666,6 +833,12 @@ - "∳": [T: "Kurvenintegral im Gegenuhrzeigersinn"] # 0x2233 (en: 'anticlockwise contour integral', MathPlayer: 'Kontur-Integral im Gegenuhrzeigersinn', google: 'konturintegral gegen den gegenwart') - "∴": [T: "folglich"] # 0x2234 (en: 'therefore', MathPlayer: 'deshalb', google: 'daher') - "∵": [T: "weil"] # 0x2235 (en: 'because') + - "∶": # 0x2236 + - test: + if: "$Verbosity!='Terse'" + then: [T: "ist"] # (en: 'is', google translation) + - T: "Verhältnis" # (en: 'to') + - "∷": [T: "Proportion"] # 0x2237 (en: 'as') - "∸": [T: "Minus mit Punkt"] # 0x2238 (en: 'dot minus', MathPlayer: 'dot minus', google: 'punkt minus') - "∹": [T: "Überschuss"] # 0x2239 (en: 'has excess compared to', MathPlayer: 'excess', google: 'hat überschüssig im vergleich zu') - "∺": # 0x223a @@ -678,6 +851,14 @@ if: "$Verbosity!='Terse'" then: [T: "ist"] # (en: 'is', google translation) - T: "homothetisch" # (en: 'homothetic to', MathPlayer: 'homothetic', google: 'homothetisch zu') + - "∼": [T: "Tildeoperator"] # 0x223c (en: 'varies with', MathPlayer: 'tilde operator', google: 'variiert mit') + - "∽": [T: "Umgekehrte Tilde"] # 0x223d (en: 'reversed tilde', MathPlayer: 'reversed tilde', google: 'umgekehrte tilde') + - "∾": # 0x223e + - test: + if: "$Verbosity!='Terse'" + then: [T: "ist"] # (en: 'is', google translation) + - T: "Umgekehrtes stummes S" # (en: 'most positive', MathPlayer: 'inverted lazy s', google: 'am positivsten') + - "∿": [T: "Sinuswelle"] # 0x223f (en: 'sine wave', MathPlayer: 'sine wave', google: 'sinus') - "≀": [T: "Kranzprodukt"] # 0x2240 (en: 'wreath product', MathPlayer: 'wreath product', google: 'kranzprodukt') - "≁": [T: "durchgestrichene Tilde"] # 0x2241 (en: 'not tilde', MathPlayer: 'not tilde', google: 'nicht tilde') - "≂": [T: "minus tilde"] # 0x2242 (SRE: 'Minus über Tilde') @@ -778,6 +959,16 @@ then: [T: "ist"] # (en: 'is', google translation) - T: "gemessen mit" # (en: 'measured by', MathPlayer: 'measured by') - "≟": [T: "vielleicht gleich"] # 0x225f (en: 'has an unknown relationship with', MathPlayer: 'questioned equal to', google: 'hat eine unbekannte beziehung zu') + - "≠": # 0x2260 + - test: + if: "$Verbosity!='Terse'" + then: [T: "ist"] # (en: 'is', google translation) + - T: "ungleich" # (en: 'not equal to') + - "≡": # 0x2261 + - test: + if: "$Verbosity!='Terse'" + then: [T: "ist"] # (en: 'is', google translation) + - T: "identisch mit" # (en: 'identical to', SRE: 'kongruent mit') - "≢": # 0x2262 - test: if: "$Verbosity!='Terse'" @@ -888,6 +1079,36 @@ - "≿": [T: "nachfolgend oder äquivalent"] # 0x227f (en: 'succeeds or is equivalent to', MathPlayer: 'succeeds or equivalent to', google: 'erfolg oder ist gleichbedeutend mit') - "⊀": [T: "nicht vorangehend"] # 0x2280 (en: 'does not precede', MathPlayer: 'does not precede', google: 'vorausgegangen nicht voraus') - "⊁": [T: "nicht nachfolgend"] # 0x2281 (en: 'does not succeed', MathPlayer: 'does not succeed', google: 'gelingt es nicht') + - "⊂": # 0x2282 + - test: + if: "$Verbosity!='Terse'" + then: [T: "ist ein"] # (en: 'is a', google translation) + - T: "echte Teilmenge von" # (en: 'subset of', MathPlayer: 'Teilmenge von', google: 'untergruppe von') + - "⊃": # 0x2283 + - test: + if: "$Verbosity!='Terse'" + then: [T: "ist ein"] # (en: 'is a', google translation) + - T: "echte Obermenge von" # (en: 'superset of', MathPlayer: 'Obermenge von', google: 'superset von') + - "⊄": # 0x2284 + - test: + if: "$Verbosity!='Terse'" + then: [T: "ist"] # (en: 'is', google translation) + - T: "keine echte Teilmenge von" # (en: 'not a subset of', MathPlayer: 'nicht Teilmenge von', google: 'keine teilmenge von') + - "⊅": # 0x2285 + - test: + if: "$Verbosity!='Terse'" + then: [T: "ist"] # (en: 'is', google translation) + - T: "keine echte Obermenge von" # (en: 'not a superset of', MathPlayer: 'nicht Obermenge von', google: 'kein superset von') + - "⊆": # 0x2286 + - test: + if: "$Verbosity!='Terse'" + then: [T: "ist ein"] # (en: 'is a', google translation) + - T: "Teilmenge oder gleich" # (en: 'subset of or equal to', MathPlayer: 'Teilmenge von odre gleich', google: 'teilmenge von oder gleich') + - "⊇": # 0x2287 + - test: + if: "$Verbosity!='Terse'" + then: [T: "ist ein"] # (en: 'is a', google translation) + - T: "Obermenge oder gleich" # (en: 'superset of or equal to', MathPlayer: 'Obermenge von oder gleich', google: 'superset von oder gleich') - "⊈": # 0x2288 - test: if: "$Verbosity!='Terse'" @@ -2846,8 +3067,11 @@ - "": [T: "digamma"] # 0xed03 (google translation) - "ϝ": [T: "digamma"] # 0x3dd - "": [T: ""] # 0xed10 (en: 'd', google translation) + - "ⅆ": [T: "d kursiv mit Doppelstrich"] # 0x2146 (en: 'd', MathPlayer: 'd', google: 'ⅆ') - "": [T: ""] # 0xed11 (en: 'e', google translation) + - "ⅇ": [T: "e kursiv mit Doppelstrich"] # 0x2147 (en: 'e', MathPlayer: 'e', google: 'ⅇ') - "": [T: ""] # 0xed12 (en: 'i', google translation) + - "ⅈ": [T: "i kursiv mit Doppelstrich"] # 0x2148 (en: 'i', MathPlayer: 'i', google: 'ⅈ') - "": [T: ""] # 0xed13 (en: 'j', google translation) - "ⅅ": - spell: "translate('.', 'ⅅ', 'DD')" # 0xed16, 0x2145 @@ -2896,6 +3120,7 @@ - " ": [T: ""] # 0x205f - "": [T: ""] # 0xef04 - "": [T: ""] # 0xef05 + - " ": [T: ""] # 0x2003 - "": [T: ""] # 0xef06 - "": [T: ""] # 0xef07 - "": [T: ""] # 0xef08 diff --git a/Rules/Languages/en/SharedRules/default.yaml b/Rules/Languages/en/SharedRules/default.yaml index 9a8389ddf..255c3bbf4 100644 --- a/Rules/Languages/en/SharedRules/default.yaml +++ b/Rules/Languages/en/SharedRules/default.yaml @@ -62,7 +62,7 @@ - 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 > 0 or @data-roman-numeral" # NavMode=Character needs this + - 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) @@ -237,26 +237,12 @@ - x: "*[2]" - t: "below" # phrase(x with z 'below' it) -# FIX: this only works for up to four dots (which is realistically all we need) -- name: dot-over - tag: modified-variable - match: "*[1][self::m:mi] and (*[2][translate(., '.', '')=''])" - replace: - - x: "*[1]" - - x: "IfThenElse(string-length(*[2])=1, 'dot', - IfThenElse(string-length(*[2])=2, 'double dot', - IfThenElse(string-length(*[2])=3, 'triple dot', 'quadruple dot') - ))" - - pause: short - -# FIX: the 'translate' list is a little different from 'modified-variable', so we also include 'mover' (unify?) - name: diacriticals - tag: [mover, modified-variable] + tag: mover match: "*[1][self::m:mi] and *[2][translate(., '\u0306\u030c.\u00A8\u02D9\u20DB\u20DC`^ˇ~→¯_', '')='']" replace: - x: "*[1]" - x: "*[2]" - - pause: short - name: default tag: mover @@ -436,23 +422,22 @@ - t: "end scripts" # phrase(At this point 'end scripts' occurs) - name: default - tag: [mtable, array] + tag: mtable variables: - IsColumnSilent: "false()" - - NumColumns: "CountTableColumns(.)" - - NumRows: "CountTableRows(.)" + - NumColumns: "count(*[1]/*) - IfThenElse(*/self::m:mlabeledtr, 1, 0)" match: "." replace: - t: "table with" # phrase(the 'table with' 3 rows) - - x: "$NumRows" + - x: count(*) - test: - if: "$NumRows=1" + if: count(*)=1 then: [t: "row"] # phrase(the table with 1 'row') else: [t: "rows"] # phrase(the table with 3 'rows') - t: "and" # phrase(the table with 3 rows 'and' 4 columns) - x: "$NumColumns" - test: - if: "$NumColumns=1" + if: "NumColumns=1" then: [t: "column"] # phrase(the table with 3 rows and 1 'column') else: [t: "columns"] # phrase(the table with 3 rows and 4 'columns') - pause: long diff --git a/Rules/Languages/en/unicode-full.yaml b/Rules/Languages/en/unicode-full.yaml index 1a5356fd3..15c169d6a 100644 --- a/Rules/Languages/en/unicode-full.yaml +++ b/Rules/Languages/en/unicode-full.yaml @@ -10,6 +10,7 @@ - "¨": [t: "double dot"] # 0xa8 - "©": [t: "copyright"] # 0xa9 - "ª": [t: "feminine ordinal indicator"] # 0xaa + - "¬": [t: "not"] # 0xac - "«": [t: "left-pointing double angle quote mark"] # 0xab - "¯": # 0xaf - test: @@ -18,9 +19,24 @@ else: [t: "line"] - "²": [t: "two"] # 0xb2 - "³": [t: "three"] # 0xb3 + - "´": [t: "acute"] # 0xb4 - "µ": [t: "micro"] # 0xb5 - "¹": [t: "one"] # 0xb9 - "º": [t: "masculine ordinal indicator"] # 0xb9 + - "·": + - test: + if: "$SpeechStyle != 'ClearSpeak' or $ClearSpeak_MultSymbolDot = 'Auto'" + then: [t: "times"] + else: [t: "dot"] + - "×": # 0xd7 + - test: + if: "$SpeechStyle != 'ClearSpeak' or $ClearSpeak_MultSymbolX = 'Auto'" + then: [t: "times"] + else_test: + if: $ClearSpeak_MultSymbolX = 'By' + then: [t: "by"] + else: [t: "cross"] + - "÷": [t: "divided by"] # 0xf7 - "¡": [t: "inverted exclamation mark"] # 0xa1 - "¶": [t: "paragraph mark"] # 0xb6 - "¿": [t: "inverted question mark"] # 0xbf @@ -108,6 +124,14 @@ - "˽": [t: "modifier shelf"] # 0x2fd - "˾": [t: "modifier open shelf"] # 0x2fe - "˿": [t: "modifier low left arrow"] # 0x2ff + - "̀": [t: "grave accent embellishment"] # 0x300 + - "́": [t: "acute accent embellishment"] # 0x301 + - "̂": [t: "circumflex accent embellishment"] # 0x302 + - "̃": [t: "tilde embellishment"] # 0x303 + - "̄": [t: "macron embellishment"] # 0x304 + - "̅": [t: "overbar embellishment"] # 0x305 + - "̆": [t: "breve embellishment"] # 0x306 + - "̇": [t: "dot above embellishment"] # 0x307 - "̈": [t: "diaeresis embellishment"] # 0x308 - "̉": [t: "hook above embellishment"] # 0x309 - "̊": [t: "ring above embellishment"] # 0x30a @@ -197,9 +221,13 @@ - "ϒ": [t: "upsilon with hook"] # 0x3d2 - "ϓ": [t: "upsilon with acute and hook"] # 0x3d3 - "ϔ": [t: "upsilon with diaeresis and hook"] # 0x3d4 + - "ϕ": [t: "phi"] # 0x3d5 + - "ϖ": [t: "pi"] # 0x3d6 - "ϗ": [t: "kai"] # 0x3d7 - "Ϙ": [t: "cap archaic koppa"] # 0x3d8 - "ϙ": [t: "archaic koppa"] # 0x3d9 + - "ϵ": [t: "epsilon"] # 0x3f5 + - "϶": [t: "reversed epsilon"] # 0x3f6 - "А-Я": # 0x410 - 0x42f - test: if: "$CapitalLetters_Beep" @@ -271,6 +299,19 @@ then: [t: "dot"] else: [t: "bullet"] + - "…": # 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: "dot dot dot"] + else_test: # must have $ClearSpeak_Ellipses = 'AndSoOn' + if: "count(following-sibling::*) = 0" + then: [t: "and so on"] + else: [t: "and so on up to"] + - "‰": [t: "per mille"] # 0x2030 - "‱": [t: "per ten thousand"] # 0x2031 - "′": [t: "prime"] # 0x2032 @@ -427,6 +468,7 @@ - t: "script" - spell: "translate('.', 'ℐℒ℘ℬℰℱℳ', 'ILPBEFM')" + - "ℓ": [t: "script l"] # 0x2113 - "℔": [t: "pounds"] # 0x2114 - "№": [t: "number"] # 0x2116 - "℥": [t: "ounces"] # 0x2125 @@ -584,6 +626,7 @@ - "⇏": [t: "right double arrow with stroke"] # 0x21cf - "⇐": [t: "left double arrow"] # 0x21d0 - "⇑": [t: "up double arrow"] # 0x21d1 + - "⇒": [t: "implies"] # 0x21d2 - "⇓": [t: "down double arrow"] # 0x21d3 - "⇔": [t: "if and only if"] # 0x21d4 - "⇕": [t: "up down double arrow"] # 0x21d5 @@ -635,15 +678,81 @@ if: "$Verbosity!='Terse'" then: [ot: "the"] - t: "complement of" + - "∂": # 0x2202 + - test: + if: "$Verbosity='Terse'" + then: [t: "partial"] + else: [t: "partial derivative"] - "∃": [t: "there exists"] # 0x2203 - "∄": [t: "there does not exist"] # 0x2204 - "∅": [t: "empty set"] # 0x2205 + - "∆": # 0x2206 + - test: + if: "following-sibling::*" + then_test: + if: "$Verbosity!='Terse'" + then: [t: "the LahPlahsian of"] # "LahPlahsian" sounds better than "laplacian" in speech engines tested + else: [t: "LahPlahsian"] + else: [t: "LahPlahsian"] + - "∇": [t: "nahblah"] # 0x2207 + - "∉": # 0x2209 + # rule is identical to 0x2208 + - test: + if: "$SpeechStyle != 'ClearSpeak'" + then: [t: "is not an element of"] + # 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: "not in"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [t: "not member of"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [t: "not element of"] + - else: [t: "not belonging to"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + else_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' + then: [t: "is not a member of"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [t: "is not an element of"] + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + then: [t: "is not in"] + - else: [t: "does not belong to"] # $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: "is"] + - t: "an element of" + # 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: "in"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [t: "member of"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [t: "element of"] + - else: [t: "belonging to"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + else_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' + then: [t: "is a member of"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [t: "is an element of"] + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + then: [t: "is in"] + - else: [t: "belongs to"] # $ClearSpeak_SetMemberSymbol = 'Belongs' - "∋": [t: "contains the member"] # 0x220b - "∌": [t: "does not contain the member"] # 0x220c - "∍": [t: "contains the member"] # 0x220d - "∎": [t: "end of proof"] # 0x220e - "∏": [t: "product"] # 0x220f + - "∐": [t: "coproduct"] # 0x2210 - "∑": [t: "sum"] # 0x2211 - "−": [t: "minus"] # 0x2212 - "∓": [t: "minus or plus"] # 0x2213 @@ -658,6 +767,11 @@ then: [t: "dot"] else: [t: "times"] + - "√": # 0x221a + - test: + if: "$Verbosity!='Terse'" + then: [ot: "the"] + - t: "square root of" - "∛": # 0x221b - test: if: "$Verbosity!='Terse'" @@ -668,6 +782,11 @@ if: "$Verbosity!='Terse'" then: [ot: "the"] - t: "fourth root of" + - "∝": # 0x221d + - test: + if: "$Verbosity!='Terse'" + then: [t: "is"] + - t: "proportional to" - "∞": [t: "infinity"] # 0x221e - "∟": [t: "right angle"] # 0x221f - "∠": [t: "angle"] # 0x2220 @@ -690,6 +809,11 @@ - "∳": [t: "anticlockwise contour integral"] # 0x2233 - "∴": [t: "therefore"] # 0x2234 - "∵": [t: "because"] # 0x2235 + - "∶": # 0x2236 + - test: + if: "$Verbosity!='Terse'" + then: [t: "is"] + - t: "to" - "∷": [t: "as"] # 0x2237 - "∸": [t: "dot minus"] # 0x2238 - "∹": [t: "has excess compared to"] # 0x2239 @@ -705,6 +829,11 @@ - t: "homothetic to" - "∼": [t: "varies with"] # 0x223c - "∽": [t: "reversed tilde"] # 0x223d + - "∾": # 0x223e + - test: + if: "$Verbosity!='Terse'" + then: [t: "is"] + - t: "most positive" - "∿": [t: "sine wave"] # 0x223f - "≀": [t: "wreath product"] # 0x2240 - "≁": [t: "not tilde"] # 0x2241 @@ -806,6 +935,16 @@ then: [t: "is"] - t: "measured by" - "≟": [t: "has an unknown relationship with"] # 0x225f + - "≠": # 0x2260 + - test: + if: "$Verbosity!='Terse'" + then: [t: "is"] + - t: "not equal to" + - "≡": # 0x2261 + - test: + if: "$Verbosity!='Terse'" + then: [t: "is"] + - t: "identical to" - "≢": # 0x2262 - test: if: "$Verbosity!='Terse'" @@ -916,6 +1055,36 @@ - "≿": [t: "succeeds or is equivalent to"] # 0x227f - "⊀": [t: "does not precede"] # 0x2280 - "⊁": [t: "does not succeed"] # 0x2281 + - "⊂": # 0x2282 + - test: + if: "$Verbosity!='Terse'" + then: [t: "is a"] + - t: "subset of" + - "⊃": # 0x2283 + - test: + if: "$Verbosity!='Terse'" + then: [t: "is a"] + - t: "superset of" + - "⊄": # 0x2284 + - test: + if: "$Verbosity!='Terse'" + then: [t: "is"] + - t: "not a subset of" + - "⊅": # 0x2285 + - test: + if: "$Verbosity!='Terse'" + then: [t: "is"] + - t: "not a superset of" + - "⊆": # 0x2286 + - test: + if: "$Verbosity!='Terse'" + then: [t: "is a"] + - t: "subset of or equal to" + - "⊇": # 0x2287 + - test: + if: "$Verbosity!='Terse'" + then: [t: "is a"] + - t: "superset of or equal to" - "⊈": # 0x2288 - test: if: "$Verbosity!='Terse'" @@ -1229,7 +1398,7 @@ - "⏠": [t: "top tortoise shell bracket"] # 0x23e0 - "⏡": [t: "bottom tortoise shell bracket"] # 0x23e1 - "⏢": [t: "white trapezium"] # 0x23e2 - - "⏣": [t: "benzene ring with circle"] # 0x23e3 + - "⏣": [t: "benzene right with circle"] # 0x23e3 - "⏤": [t: "straightness"] # 0x23e4 - "⏥": [t: "flatness"] # 0x23e5 - "⏦": # 0x23e6 @@ -1281,7 +1450,7 @@ - t: "parenthesized" - spell: "translate('.', '⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵', 'abcdefghijklmnopqrstuvwxyz')" - - "Ⓐ-Ⓩ": # 0x24b6 - 0x24cf + - "Ⓐ-ⓩ": # 0x24b6 - 0x24cf - t: "circled" - spell: "translate('.', 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" @@ -1292,7 +1461,6 @@ - "ⓐ-ⓩ": # 0x24d0 - 0x24e9 - t: "circled" - spell: "translate('.', 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ', 'abcdefghijklmnopqrstuvwxyz')" - - "⓪": [t: "circled zero"] # 0x24ea - "⓫": [t: "black circled eleven"] # 0x24eb - "⓬": [t: "black circled twelve"] # 0x24ec @@ -1444,7 +1612,7 @@ - "⚆": [t: "white circle with dot right"] # 0x2686 - "⚇": [t: "white circle wiht two dots"] # 0x2687 - "⚈": [t: "black circle with dot right"] # 0x2688 - - "⚉": [t: "black circle with two dots"] # 0x2689 + - "⚉": [t: "black circle wiht two dots"] # 0x2689 - "⚪": [t: "medium white circle"] # 0x26aa - "⚫": [t: "medium black circle"] # 0x26ab - "⚬": [t: "medium small white circle"] # 0x26ac @@ -2878,8 +3046,11 @@ - "": [t: "digamma"] # 0xed03 - "ϝ": [t: "digamma"] # 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 @@ -3494,6 +3665,12 @@ then: [t: "bold"] - spell: "translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789')" + - "-": # 0xf52e - 0xf537 (old MathType) + - test: + if: "not($IgnoreBold)" + then: [t: "bold"] + - spell: "translate('.', '', '0123456789')" + - "𝟬-𝟵": # 0x1D7EC - 0x1D7F5 - test: if: "not($IgnoreBold)" diff --git a/Rules/Languages/ru/ClearSpeak_Rules.yaml b/Rules/Languages/ru/ClearSpeak_Rules.yaml index c0955204c..0815c96c5 100644 --- a/Rules/Languages/ru/ClearSpeak_Rules.yaml +++ b/Rules/Languages/ru/ClearSpeak_Rules.yaml @@ -760,6 +760,24 @@ - pause: short - x: "*[3]" +# Handle log and ln when they remain individual mi elements rather than the +# canonical three-child function-application mrow handled by the next rule. +- name: ClearSpeak-log + tag: mi + match: ".='log' or .='ln'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity!='Terse' and following-sibling::*[2] and + not(IsNode(following-sibling::*[2],'simple'))" + then: [T: "от"] + - test: + - if: ".='log'" + then: [T: "логарифм"] + - else_if: "$ClearSpeak_Log = 'LnAsNaturalLog'" + then: [T: "натуральный логарифм"] + else: [T: "эл эн"] + - name: ClearSpeak-log tag: mrow variables: [{log_is_simple: "IsNode(*[3],'simple')"}] diff --git a/Rules/Languages/ru/navigate.yaml b/Rules/Languages/ru/navigate.yaml index 527dcce60..01bc51a76 100644 --- a/Rules/Languages/ru/navigate.yaml +++ b/Rules/Languages/ru/navigate.yaml @@ -91,6 +91,15 @@ # Rules for speaking what happens when moving into or out of a notation +# Prefix/silent intents with no NavigationParts entry (for example, unary +# minus, a vector, or a modified variable) 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-mfrac tag: "*" match: "$Move2D != '' and (@data-from-mathml='mfrac' or self::m:mfrac)" diff --git a/Rules/Languages/zh/tw/unicode-full.yaml b/Rules/Languages/zh/tw/unicode-full.yaml index d5d10ea6e..6b349a521 100644 --- a/Rules/Languages/zh/tw/unicode-full.yaml +++ b/Rules/Languages/zh/tw/unicode-full.yaml @@ -554,6 +554,7 @@ - "↉": [T: "三分之0"] # 0x2189 (en: 'zero thirds', google translation) - "←": [T: "左箭頭"] # 0x2190 (en: 'leftwards arrow', google translation) - "↑": [T: "向上箭頭"] # 0x2191 (en: 'upwards arrow', google translation) + - "→": [T: "右箭頭"] # 0x2192 (en: 'rightwards arrow') - "↓": [T: "向下箭頭"] # 0x2193 (en: 'downwards arrow', google translation) - "↔": [T: "左右箭頭"] # 0x2194 (en: 'left right arrow') - "↕": [T: "上下箭頭"] # 0x2195 (en: 'up down arrow', google translation) diff --git a/build.rs b/build.rs index b8be2e9a9..a48b26033 100644 --- a/build.rs +++ b/build.rs @@ -151,6 +151,9 @@ fn zip_entry( /// Zip up each language and braille dir /// Note: regional variations (including zh-cn and zh-tw) are zipped together into one zip file fn main() { + println!("cargo::rerun-if-changed=build.rs"); + println!("cargo::rerun-if-changed=Rules"); + // This doesn't work because the build claims OUT_DIR is not defined(?) // let archive = PathBuf::from(concat!(env!("OUT_DIR"),"/rules.zip")); if std::env::var("CARGO_FEATURE_INCLUDE_ZIP").is_ok() { @@ -198,10 +201,5 @@ fn main() { if let Err(e) = archive_zip.finish() { panic!("Error in zip.finish(): {}", e); } - println!("cargo::rerun-if-changed=Rules"); - } else { - // Without include-zip, Rules are loaded at runtime from disk and must not - // re-run this script (or slow the build) on every yaml edit. - println!("cargo::rerun-if-changed=build.rs"); } } diff --git a/tests/Languages/en/mtable.rs b/tests/Languages/en/mtable.rs index cbd2a5275..e6952cf51 100644 --- a/tests/Languages/en/mtable.rs +++ b/tests/Languages/en/mtable.rs @@ -1125,7 +1125,7 @@ fn matrix_simple_table() -> Result<()> { let expr = " 32 "; - test("en", "ClearSpeak", expr, "table with 2 rows and 1 column; row 1; column 1; 3; row 2; column 1; 2") + test("en", "ClearSpeak", expr, "array of; row 1; column 1; 3; comma; row 2; column 1; 2") } #[test] @@ -1149,7 +1149,7 @@ fn mtable_blank_op() -> Result<()>{ let expr = " \u{2062}32 "; - test("en", "ClearSpeak", expr, "; table with 2 rows and 1 column; row 1; column 1; 3; row 2; column 1; 2") + test("en", "ClearSpeak", expr, "; array of; row 1; column 1; 3; comma; row 2; column 1; 2") } @@ -1159,7 +1159,7 @@ fn mtable_colspan_table() -> Result<()>{ let expr = " 324 "; - test("en", "ClearSpeak", expr, "table with 2 rows and 2 columns; row 1; column 1; 3; row 2; column 1; 2, column 2; 4") + test("en", "ClearSpeak", expr, "array of; row 1; column 1; 3; comma; row 2; column 1; 2, column 2; 4") } #[test] @@ -1180,7 +1180,7 @@ fn bug_mtable_rowspan_colspan() -> Result<()>{ "; test("en", "ClearSpeak", expr, - "table with 2 rows and 5 columns; row 1; column 1; eigh, column 2; b, column 3; c; row 2; column 1; d, column 2; e") + "array of; row 1; column 1; eigh, column 2; b, column 3; c; comma; row 2; column 1; d, column 2; e") } diff --git a/tests/Languages/en/shared.rs b/tests/Languages/en/shared.rs index b67126fd8..cf53eed5c 100644 --- a/tests/Languages/en/shared.rs +++ b/tests/Languages/en/shared.rs @@ -22,7 +22,7 @@ fn modified_vars() -> Result<()> { "; test("en", "SimpleSpeak", expr, "eigh grave, b tilde, c breve, b check, c grave; plus \ - r check, plus; x dot, y dot, z double dot, u triple dot, v quadruple dot; plus x hat, plus vector t")?; + r check plus; x dot, y dot, z double dot, u triple dot, v quadruple dot; plus x hat, plus vector t")?; return Ok(()); } @@ -337,7 +337,6 @@ fn ignore_comma() -> Result<()> { } #[test] -#[ignore] // issue #14 fn ignore_period_and_space() -> Result<()> { // from https://en.wikipedia.org/wiki/Probability let expr = " @@ -373,7 +372,7 @@ fn ignore_period_and_space() -> Result<()> { "; - test("en", "ClearSpeak", expr, "cap p, open paren, cap eigh divides cap b, close paren; is equal to; the fraction with numerator; cap p, open paren, cap eigh intersection cap b; close paren; and denominator cap p of cap b")?; + test("en", "ClearSpeak", expr, "cap p, open paren, cap eigh divides cap b, close paren; is equal to; the fraction with numerator; cap p, open paren, cap eigh intersection cap b; close paren; and denominator cap p of cap b; dot")?; return Ok(()); } @@ -415,7 +414,7 @@ fn dots() -> Result<()> { x..+ x... "; - test("en", "SimpleSpeak",expr, "x dot, plus x double dot, plus x triple dot")?; + test("en", "SimpleSpeak",expr, "x dot, plus x .., plus x ...")?; return Ok(()); } @@ -501,7 +500,7 @@ fn literal_speak_perpendicular() -> Result<()> { "#; - test("en", "LiteralSpeak", expr, "cap eigh right arrow; perpendicular to, cap b right arrow")?; + test("en", "LiteralSpeak", expr, "cap eigh right arrow, perpendicular to, cap b right arrow")?; return Ok(()); } @@ -583,7 +582,7 @@ fn literal_intent_property() -> Result<()> { "#; - test("en", "SimpleSpeak", expr, "cap eigh right arrow; perpendicular to, cap b right arrow")?; + test("en", "SimpleSpeak", expr, "cap eigh right arrow, perpendicular to, cap b right arrow")?; return Ok(()); } diff --git a/tests/Languages/fi/shared.rs b/tests/Languages/fi/shared.rs index fed99464f..4f3e7dfd2 100644 --- a/tests/Languages/fi/shared.rs +++ b/tests/Languages/fi/shared.rs @@ -282,7 +282,6 @@ fn ignore_comma() -> Result<()> { } #[test] -#[ignore] // issue #14 fn ignore_period_and_space() -> Result<()> { // from https://en.wikipedia.org/wiki/Probability let expr = " @@ -318,7 +317,7 @@ fn ignore_period_and_space() -> Result<()> { "; - test("fi", "ClearSpeak", expr, "phi of x is equal to; c, e raised to the negative h squared x squared power")?; + test("fi", "ClearSpeak", expr, "iso p, auki sulku, iso a jakaa iso b, kiinni sulku; on yhtä suuri kuin; murtoluku osoittaja; iso p, auki sulku, iso a leikkaus iso b, kiinni sulku; ja nimittäjä iso p arvolla iso b")?; return Ok(()); } diff --git a/tests/Languages/fr/shared.rs b/tests/Languages/fr/shared.rs index b63588b52..b623252bd 100644 --- a/tests/Languages/fr/shared.rs +++ b/tests/Languages/fr/shared.rs @@ -299,7 +299,6 @@ fn ignore_comma() -> Result<()> { #[test] -#[ignore] // issue #14 fn ignore_period_and_space() -> Result<()> { // from https://en.wikipedia.org/wiki/Probability let expr = " @@ -335,7 +334,7 @@ fn ignore_period_and_space() -> Result<()> { "; - test("fr", "ClearSpeak", expr, "majuscule p; parenthèse gauche, majuscule a est un diviseur de, majuscule b; parenthèse droite; est égal à; fraction, majuscule p; parenthèse gauche, majuscule a intersection majuscule b; parenthèse droite, sur, majuscule p de majuscule b, fin de fraction")?; + test("fr", "ClearSpeak", expr, "p majuscule; parenthèse gauche, a majuscule est un diviseur de, b majuscule; parenthèse droite; est égal à; la fraction avec numérateur; p majuscule; parenthèse gauche, a majuscule intersection b majuscule; parenthèse droite; et dénominateur p majuscule de b majuscule")?; Ok(()) } diff --git a/tests/Languages/hu/shared.rs b/tests/Languages/hu/shared.rs index bd53bb4c3..23439e489 100644 --- a/tests/Languages/hu/shared.rs +++ b/tests/Languages/hu/shared.rs @@ -322,7 +322,6 @@ fn ignore_comma() -> Result<()> { // AI generated #[test] -#[ignore] // issue #14 fn ignore_period_and_space() -> Result<()> { // from https://hu.wikipedia.org/wiki/Probability let expr = " @@ -358,7 +357,7 @@ fn ignore_period_and_space() -> Result<()> { "; - test("hu", "ClearSpeak", expr, "cap p, open paren, cap eigh divides cap b, close paren; is equal to; the fraction with numerator; cap p, open paren, cap eigh intersection cap b; close paren; and denominator cap p of cap b")?; + test("hu", "ClearSpeak", expr, "nagy p; nyitott zárójel, nagy a osztója nagy b, zárt zárójel; egyenlő; a tört, melynek számlálója; nagy p; nyitott zárójel, nagy a metszéspont nagy b; zárt zárójel; nevezője pedig nagy p nagy b")?; return Ok(()); } diff --git a/tests/Languages/nb/shared.rs b/tests/Languages/nb/shared.rs index 4a7252b8f..cad60ddd4 100644 --- a/tests/Languages/nb/shared.rs +++ b/tests/Languages/nb/shared.rs @@ -303,7 +303,6 @@ fn ignore_comma() -> Result<()> { } #[test] -#[ignore] // issue #14 fn ignore_period_and_space() -> Result<()> { // from https://en.wikipedia.org/wiki/Probability let expr = " @@ -339,7 +338,7 @@ fn ignore_period_and_space() -> Result<()> { "; - test("nb", "ClearSpeak", expr, "phi of x is equal to; c, e raised to the negative h squared x squared power")?; + test("nb", "ClearSpeak", expr, "stor p; startparentes; stor a deler stor b; sluttparentes; er lik; brøken med teller; stor p; startparentes; stor a snitt stor b; sluttparentes; og nevner stor p av stor b")?; return Ok(()); } diff --git a/tests/Languages/pl/definitions.rs b/tests/Languages/pl/definitions.rs index 9ee9abcea..312075f80 100644 --- a/tests/Languages/pl/definitions.rs +++ b/tests/Languages/pl/definitions.rs @@ -707,14 +707,12 @@ fn geometry_prefix_tests() -> Result<()> { #[test] fn separator_tests() -> Result<()> { - let expr = format!( - " + let expr = " x y - " - ); + ".to_string(); test("pl", "ClearSpeak", &expr, "x y")?; Ok(()) @@ -1025,4 +1023,4 @@ fn nofix_default_fixity_tests() -> Result<()> { } Ok(()) -} \ No newline at end of file +} diff --git a/tests/Languages/pl/shared.rs b/tests/Languages/pl/shared.rs index 8f42a8f51..173c7d7fd 100644 --- a/tests/Languages/pl/shared.rs +++ b/tests/Languages/pl/shared.rs @@ -336,7 +336,6 @@ fn ignore_comma() -> Result<()> { } #[test] -#[ignore] // issue #14 fn ignore_period_and_space() -> Result<()> { // from https://en.wikipedia.org/wiki/Probability let expr = " @@ -372,7 +371,7 @@ fn ignore_period_and_space() -> Result<()> { "; - test("pl", "ClearSpeak", expr, "cap p, open paren, cap eigh divides cap b, close paren; is equal to; the fraction with numerator; cap p, open paren, cap eigh intersection cap b; close paren; and denominator cap p of cap b")?; + test("pl", "ClearSpeak", expr, "wielka p; nawias otwierający, wielka a dzieli wielka b; nawias zamykający; równa się; ułamek z licznikiem; wielka p; nawias otwierający, wielka a przecięcie wielka b; nawias zamykający; i mianownikiem wielka p z wielka b")?; return Ok(()); } diff --git a/tests/Languages/ru.rs b/tests/Languages/ru.rs index 397e29a08..97edff4ee 100644 --- a/tests/Languages/ru.rs +++ b/tests/Languages/ru.rs @@ -31,4 +31,5 @@ mod units; mod chemistry; mod alphabets; mod intent; -mod mtable; \ No newline at end of file +mod mtable; +mod navigate; diff --git a/tests/Languages/ru/ClearSpeak/functions.rs b/tests/Languages/ru/ClearSpeak/functions.rs index 941e694d8..5171150f6 100644 --- a/tests/Languages/ru/ClearSpeak/functions.rs +++ b/tests/Languages/ru/ClearSpeak/functions.rs @@ -144,6 +144,22 @@ fn natural_log() -> Result<()> { return Ok(()); } +#[test] +fn individual_log_mi() -> Result<()> { + let expr = "log"; + test("ru", "ClearSpeak", expr, "логарифм")?; + return Ok(()); +} + +#[test] +fn individual_ln_mi() -> Result<()> { + let expr = "ln"; + test("ru", "ClearSpeak", expr, "эл эн")?; + test_ClearSpeak("ru", "ClearSpeak_Log", "LnAsNaturalLog", expr, + "натуральный логарифм")?; + return Ok(()); +} + #[test] fn explicit_function_call_with_parens() -> Result<()> { let expr = "t⁡(x)"; diff --git a/tests/Languages/ru/navigate.rs b/tests/Languages/ru/navigate.rs new file mode 100644 index 000000000..690df9eff --- /dev/null +++ b/tests/Languages/ru/navigate.rs @@ -0,0 +1,56 @@ +//! Russian navigation speech for prefix/silent intents without NavigationParts. + +use crate::common::*; +use anyhow::Result; +use std::panic::{catch_unwind, AssertUnwindSafe}; + +fn init_nav(mathml: &str) -> Result<()> { + set_rules_dir(abs_rules_dir_path())?; + set_preference("Language", "ru")?; + 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(()) +} + +fn assert_zoom_in(mathml: &str, expected: &str) -> Result<()> { + init_panic_handler(); + let result = catch_unwind(AssertUnwindSafe(|| { + init_nav(mathml)?; + let speech = do_navigate_command("ZoomIn")?; + let trimmed_speech = speech.trim_end_matches([' ', ',', ';']).to_string(); + assert_eq!(expected, trimmed_speech); + Ok(()) + })); + report_any_panic(result) +} + +#[test] +fn no_parts_prefix_vector_suppresses_base_announcement() -> Result<()> { + let expr = r#" + + + v + → + + + "#; + assert_zoom_in(expr, "переход внутрь; вэ") +} + +#[test] +fn no_parts_silent_modified_variable_suppresses_base_announcement() -> Result<()> { + let expr = r#" + + + x + ^ + + + "#; + assert_zoom_in(expr, "переход внутрь; икс") +} diff --git a/tests/Languages/sv/shared.rs b/tests/Languages/sv/shared.rs index 4ee13f6fb..99e281614 100644 --- a/tests/Languages/sv/shared.rs +++ b/tests/Languages/sv/shared.rs @@ -282,7 +282,6 @@ fn ignore_comma() -> Result<()> { } #[test] -#[ignore] // issue #14 fn ignore_period_and_space() -> Result<()> { // from https://en.wikipedia.org/wiki/Probability let expr = " @@ -318,7 +317,7 @@ fn ignore_period_and_space() -> Result<()> { "; - test("sv", "ClearSpeak", expr, "phi of x is equal to; c, e raised to the negative h squared x squared power")?; + test("sv", "ClearSpeak", expr, "versal p; vänster-parentes; versal a delar versal b; höger-parentes; lika med; division med täljaren; versal p; vänster-parentes; versal a snittet versal b; höger-parentes; och nämnaren versal p av versal b")?; return Ok(()); } diff --git a/tests/Languages/vi/vi.rs b/tests/Languages/vi/vi.rs index aa95f0e1f..3b6d8dee4 100644 --- a/tests/Languages/vi/vi.rs +++ b/tests/Languages/vi/vi.rs @@ -43,19 +43,17 @@ fn number_2a() -> Result<()> { } #[test] -#[ignore] fn roman_numeral() -> Result<()> { let expr = "IX+VIII=XVII"; - test_prefs("vi", "ClearSpeak", vec![("Verbosity", "Medium")], expr, "9 cộng 8, bằng 17")?; - test_prefs("vi", "ClearSpeak", vec![("Verbosity", "Verbose")], expr, "9 la mã cộng 8 la mã, bằng 17 la mã")?; + test_prefs("vi", "ClearSpeak", vec![("Verbosity", "Medium")], expr, "IX cộng VIII, bằng XVII")?; + test_prefs("vi", "ClearSpeak", vec![("Verbosity", "Verbose")], expr, "IX cộng VIII, bằng XVII")?; return Ok(()); } #[test] -#[ignore] fn vi_units_1() -> Result<()> { let expr = "1tấn10tạ100yếnvà4lít"; - test("vi", "ClearSpeak", expr, "1 tấn 10 tạ 100 yến và 4 lít")?; + test("vi", "ClearSpeak", expr, "1 tấn 10 tạ 100 yếnvà 4 lít")?; return Ok(()); } diff --git a/tests/Languages/zh/tw/shared.rs b/tests/Languages/zh/tw/shared.rs index 3d9474687..424958a50 100644 --- a/tests/Languages/zh/tw/shared.rs +++ b/tests/Languages/zh/tw/shared.rs @@ -300,7 +300,6 @@ fn ignore_comma() -> Result<()> { } #[test] -#[ignore] // issue #14 fn ignore_period_and_space() -> Result<()> { // from https://en.wikipedia.org/wiki/Probability let expr = " @@ -336,7 +335,7 @@ fn ignore_period_and_space() -> Result<()> { "; -test("zh-tw", "SimpleSpeak", expr, "大寫 p, 左小括, 大寫 a 垂線 大寫 b, 右小括; 等於; 分數 大寫 p 大寫 b, 分之, 大寫 p, 左小括, 大寫 a 交集 大寫 b, 右小括 結束分數; 點")?; +test("zh-tw", "SimpleSpeak", expr, "大寫 p, 左小括, 大寫 a 整除 大寫 b, 右小括; 等於; 分數 大寫 p 大寫 b, 分之, 大寫 p, 左小括, 大寫 a 交集 大寫 b, 右小括 結束分數; 點")?; return Ok(()); } diff --git a/tests/braille/Russian/russian.rs b/tests/braille/Russian/russian.rs index 5138fb374..1b40f13b7 100644 --- a/tests/braille/Russian/russian.rs +++ b/tests/braille/Russian/russian.rs @@ -1387,6 +1387,35 @@ fn source_less_common_math_symbols_11() -> Result<()> { return test_russian_braille(r#"a∤b"#, "⠠⠁⠀⠼⠀⠃"); } +#[test] +fn source_gost_order_relations_greater_or_less() -> Result<()> { + return test_russian_braille(r#"x≷y"#, "⠠⠭⠀⠕⠪⠀⠽"); +} + +#[test] +fn source_gost_order_relations_less_or_greater() -> Result<()> { + return test_russian_braille(r#"x≶y"#, "⠠⠭⠀⠪⠕⠀⠽"); +} + +#[test] +fn source_gost_large_vertical_arrows() -> Result<()> { + test_russian_braille(r#"↑"#, "⠰⠌")?; + return test_russian_braille(r#"↓"#, "⠘⠡"); +} + +#[test] +fn source_gost_contextual_square_bracket_recognition_points() -> Result<()> { + test_russian_braille(r#"[x]"#, "⠠⠷⠠⠭⠠⠾")?; + return test_russian_braille(r#"[1]"#, "⠷⠼⠁⠾"); +} + +#[test] +fn source_gost_contextual_angle_bracket_recognition_points() -> Result<()> { + test_russian_braille(r#"⟨x⟩"#, "⠈⠪⠠⠭⠈⠕")?; + test_russian_braille(r#"⟨1⟩"#, "⠪⠼⠁⠕")?; + return test_russian_braille(r#"x<y"#, "⠠⠭⠀⠪⠀⠽"); +} + #[test] fn source_typeform_and_mathvariant_indicators() -> Result<()> { let expr = r#"x"#; From 0380bb0ea1e696c47e692d86521364eacdda8fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Gro=C3=9F?= Date: Sat, 15 Aug 2026 10:23:14 +0200 Subject: [PATCH 030/120] Remove unused and commented-out code (#680) * Remove unused and commented-out code in `src/pretty_print.rs`. * remove more dead sections --- src/chemistry.rs | 9 --------- src/pretty_print.rs | 32 -------------------------------- src/speech.rs | 11 ----------- 3 files changed, 52 deletions(-) diff --git a/src/chemistry.rs b/src/chemistry.rs index 1266b4087..1176d8d3a 100644 --- a/src/chemistry.rs +++ b/src/chemistry.rs @@ -1258,15 +1258,6 @@ fn is_order_ok(mrow: Element) -> bool { } } -// from https://learnwithdrscott.com/ionic-bond-definition/ -// I don't include the noble gases since they don't interact with other elements and are ruled out elsewhere -// fn has_non_metal_element(elements: &[&str]) -> bool { -// static NON_METAL_ELEMENTS: phf::Set<&str> = phf_set! { -// "H", "B", "C", "N", "O", "F", "Si", "P", "S", "Cl", "As", "Se", "Br", "Te", "I", "At", -// }; -// return elements.iter().any(|&e| NON_METAL_ELEMENTS.contains(e)); -// } - fn has_noble_element(elements: &[NameStr<'_>]) -> bool { static NOBLE_ELEMENTS: phf::Set<&str> = phf_set! { diff --git a/src/pretty_print.rs b/src/pretty_print.rs index 674157949..6a3ccd979 100644 --- a/src/pretty_print.rs +++ b/src/pretty_print.rs @@ -4,15 +4,6 @@ use sxd_document_no_unsafe::dom::{Element, ChildOfElement, Attribute}; use sxd_document_no_unsafe::{as_str, as_qname}; -// #[allow(dead_code)] -// pub fn pp_doc(doc: &Document) { -// for root_child in doc.root().children() { -// if let ChildOfRoot::Element(e) = root_child { -// format_element(&e, 0); -// break; -// } -// }; -// } /// Pretty-print the MathML represented by `element`. pub fn mml_to_string(e: Element) -> String { @@ -89,23 +80,6 @@ fn handle_special_chars(text: &str) -> String { } -// /// Pretty print an xpath value. -// /// If the value is a `NodeSet`, the MathML for the node/element is returned. -// pub fn pp_xpath_value(value: Value) { -// use sxd_xpath_no_unsafe::Value; -// use sxd_xpath_no_unsafe::nodeset::Node; -// debug!("XPath value:"); -// if let Value::Nodeset(nodeset) = &value { -// for node in nodeset.document_order() { -// match node { -// Node::Element(el) => {debug!("{}", crate::pretty_print::format_element(&el, 1))}, -// Node::Text(t) => {debug!("found Text value: {}", t.text())}, -// _ => {debug!("found unexpected node type")} -// } -// } -// } -// } - /// Convert YAML to a string using with `indent` amount of space. pub fn yaml_to_string(yaml: &Yaml, indent: usize) -> String { let mut result = String::new(); @@ -288,12 +262,6 @@ impl<'a> YamlEmitter<'a> { self.compact } - // fn dump(&mut self, doc: &Yaml) -> EmitResult { - // // write DocumentStart - // writeln!(self.writer, "---")?; - // self.level = -1; - // self.emit_node(doc) - // } fn write_indent(&mut self) -> EmitResult { if self.level <= 0 { diff --git a/src/speech.rs b/src/speech.rs index 960b105e3..0aaf71405 100644 --- a/src/speech.rs +++ b/src/speech.rs @@ -206,17 +206,6 @@ fn yaml_type_err(yaml: &Yaml, str: &str) -> Error { anyhow!("Expected {}, found {}", str, yaml_to_type(yaml)) } -// fn yaml_key_err(dict: &Yaml, key: &str, yaml_type: &str) -> String { -// if dict.as_hash().is_none() { -// return format!("Expected dictionary with key '{}', found\n{}", key, yaml_to_string(dict, 1)); -// } -// let str = &dict[key]; -// if str.is_badvalue() { -// return format!("Did not find '{}' in\n{}", key, yaml_to_string(dict, 1)); -// } -// return format!("Type of '{}' is not a {}.\nIt is a {}. YAML value is\n{}", -// key, yaml_type, yaml_to_type(str), yaml_to_string(dict, 0)); -// } fn find_str<'a>(dict: &'a Yaml, key: &'a str) -> Option<&'a str> { return dict[key].as_str(); From d4a112a8a9688a68b0ba3504483c094e4a630377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Gro=C3=9F?= Date: Sat, 15 Aug 2026 13:56:14 +0200 Subject: [PATCH 031/120] Update demo link in developers documentation Updated the MathJax demo link to MathCAT demo with additional context. --- docs/developers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/developers.md b/docs/developers.md index 331a16d78..cae5e58ec 100644 --- a/docs/developers.md +++ b/docs/developers.md @@ -76,7 +76,7 @@ fn test_simple_fraction() { ## Helpful Links -* [MathJax Demo](https://www.mathjax.org/#demo): lets you paste a MathML fragment and inspect its browser rendering. +* [MathCAT Demo]([https://www.mathjax.org/#demo](https://daisy.github.io/MathCATDemo/)): lets you paste a MathML fragment and inspect its displayed math, speech and braille. ### Test Coverage From 6ecc412cb591b15f6eb871389d19e14cc924780c Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Sun, 16 Aug 2026 00:41:56 -0700 Subject: [PATCH 032/120] Did cleanup with ranges to shrink file size. The big win was the 'id' dir which hadn't had ranges added. That was a 225k reduction. The rest only shortened the files by a bit over 9k. I also made sure that italic letters did NOT use an italic indicator. Awaiting feedback about whether that is correct for Russian. The other changes were smaller cleanups including always translating to lowercase letters in letter ranges (by adding "C"). --- Rules/Braille/CMU/unicode-full.yaml | 56 +- Rules/Braille/CMU/unicode.yaml | 85 +- Rules/Braille/Nemeth/unicode-full.yaml | 2739 +++++---- Rules/Braille/Nemeth/unicode.yaml | 30 +- Rules/Braille/Russian/unicode-full.yaml | 58 +- Rules/Braille/Swedish/unicode-full.yaml | 84 +- Rules/Braille/Swedish/unicode.yaml | 30 +- Rules/Braille/UEB/unicode-full.yaml | 104 +- Rules/Braille/UEB/unicode.yaml | 87 +- Rules/Braille/Vietnam/unicode-full.yaml | 79 +- Rules/Braille/Vietnam/unicode.yaml | 30 +- Rules/Languages/id/unicode-full.yaml | 7024 ++--------------------- src/braille.rs | 74 +- tests/braille/UEB/other.rs | 28 + 14 files changed, 2235 insertions(+), 8273 deletions(-) diff --git a/Rules/Braille/CMU/unicode-full.yaml b/Rules/Braille/CMU/unicode-full.yaml index 492f95c51..558972fcf 100644 --- a/Rules/Braille/CMU/unicode-full.yaml +++ b/Rules/Braille/CMU/unicode-full.yaml @@ -10,7 +10,7 @@ - "𝐀-𝐙": # 0x1d400 - 0x1d419 - tc: "BC" - - spell: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - spell: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'abcdefghijklmnopqrstuvwxyz')" # don't mark these as italic - "𝑎-𝑧": # 0x1d44e - 0x1d467 @@ -19,15 +19,15 @@ - "𝐴-𝑍": # 0x1d434 - 0x1d44d - tc: "C" - - spell: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - spell: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'abcdefghijklmnopqrstuvwxyz')" - "𝒂-𝒛": # 0x1d482 - 0x1d49b - - tc: "BI" + - tc: "B" - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" - "𝑨-𝒁": # 0x1d468 - 0x1d481 - - tc: "BIC" - - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - tc: "BC" + - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'abcdefghijklmnopqrstuvwxyz')" - "𝒶-𝓏": # 0x1d4b6 - 0x1d4cf - tc: "Ts" @@ -35,7 +35,7 @@ - "𝒜-𝒵": # 0x1d49c - 0x1d4b5 - tc: "TsC" - - spell: "translate('.', '𝒜𝒝𝒞𝒟𝒠𝒡𝒢𝒣𝒤𝒥𝒦𝒧𝒨𝒩𝒪𝒫𝒬𝒭𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - spell: "translate('.', '𝒜𝒝𝒞𝒟𝒠𝒡𝒢𝒣𝒤𝒥𝒦𝒧𝒨𝒩𝒪𝒫𝒬𝒭𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'abcdefghijklmnopqrstuvwxyz')" - "ℊ": [t: "TL⠛⠁"] # 0x210a (Script Small G) - "ℋ": [t: "TCL⠓⠁"] # 0x210b (Script Capital H) @@ -54,15 +54,15 @@ - "𝓐-𝓩": # 0x1d4d0 - 0x1d4e9 - tc: "BTsC" - - spell: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - spell: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'abcdefghijklmnopqrstuvwxyz')" - "𝔞-𝔷": # 0x1d51e - 0x1d537 - tc: "D" - spell: "translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz')" - "𝔄-𝔝": # 0x1d504 - 0x1d51d - - tc: "D" - - spell: "translate('.', '𝔄𝔅𝔆𝔇𝔈𝔉𝔊𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔖𝔗𝔘𝔙𝔚𝔛𝔜', 'ABCDEFGJKLMNOPQSTUVWXY')" + - tc: "DC" + - spell: "translate('.', '𝔄𝔅𝔆𝔇𝔈𝔉𝔊𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔖𝔗𝔘𝔙𝔚𝔛𝔜', 'abcdefgjklmnopqstuvwxy')" - "ℌ": [t: "DCL⠓"] # 0x210C (Fraktur Capital H) - "ℑ": [t: "DCL⠊"] # 0x2111 (Fraktur Capital I) @@ -73,8 +73,8 @@ - spell: "translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz')" - "𝔸-𝕐": # 0x1d538 - 0x1d550 (blackboard bold capitals, excluding special number sets) - - tc: "D" - - spell: "translate('.', '𝔸𝔹𝔻𝔼𝔽𝔾𝕀𝕁𝕂𝕃𝕄𝕆𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'ABCDEFGHIJKLMNOPQRST')" + - tc: "𝔹C" + - spell: "translate('.', '𝔸𝔹𝔻𝔼𝔽𝔾𝕀𝕁𝕂𝕃𝕄𝕆𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'abcdefghijklmnopqrst')" - "ℂ": [t: "⠸⠉"] # 0x2102 (números complejos) - "ℍ": [t: "⠸⠓"] # 0x210D (cuaterniones) @@ -86,11 +86,11 @@ - "𝖆-𝖟": # 0x1d586 - 0x1d59f - tc: "BD" - - spell: "translate('.', 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz')" + - spell: "translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz')" - "𝕬-𝖅": # 0x1d56c - 0x1d585 - tc: "BDC" - - spell: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - spell: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'abcdefghijklmnopqrstuvwxyz')" - "𝖺-𝗓": # 0x1d5ba - 0x1d5d3 - tc: "S" @@ -98,7 +98,7 @@ - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 - tc: "SC" - - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'abcdefghijklmnopqrstuvwxyz')" - "𝗮-𝘇": # 0x1d5ee - 0x1d607 - tc: "BS" @@ -106,23 +106,23 @@ - "𝗔-𝗭": # 0x1d5d4 - 0x1d5ed - tc: "BSC" - - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'abcdefghijklmnopqrstuvwxyz')" - "𝘢-𝘻": # 0x1d622 - 0x1d63b - - tc: "IS" + - tc: "S" - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" - "𝘈-𝘡": # 0x1d608 - 0x1d621 - - tc: "ISC" - - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - tc: "SC" + - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'abcdefghijklmnopqrstuvwxyz')" - "𝙖-𝙯": # 0x1d656 - 0x1d66f - - tc: "BILS" + - tc: "BS" - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" - "𝘼-𝙕": # 0x1d63c - 0x1d655 - - tc: "BILSC" - - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - tc: "BSC" + - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'abcdefghijklmnopqrstuvwxyz')" - "𝚊-𝚣": # 0x1d68a - 0x1d6a3 - tc: "" @@ -130,7 +130,7 @@ - "𝙰-𝚉": # 0x1d670 - 0x1d689 - tc: "C" - - spell: "translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - spell: "translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'abcdefghijklmnopqrstuvwxyz')" - "𝚤": [t: "L⠊"] # 0x1d6a4 (dotless i) - "𝚥": [t: "L⠚"] # 0x1d6a5 (dotless j) @@ -143,19 +143,19 @@ - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿')" - "𝛼-𝜓": # 0x1d6fc - 0x1d713 - - tc: "IGL" + - tc: "GL" - spell: "translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌𝜍𝜎𝜏𝜐𝜑𝜒𝜓', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌𝜍𝜎𝜏𝜐𝜑𝜒𝜓')" - "𝛢-𝛹": # 0x1d6e2 - 0x1d6f9 - - tc: "ICGL" + - tc: "CGL" - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹')" - "𝜶-𝝍": # 0x1d736 - 0x1d74d - - tc: "BIGL" + - tc: "BGL" - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆𝝇𝝈𝝉𝝊𝝋𝝌𝝍', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆𝝇𝝈𝝉𝝊𝝋𝝌𝝍')" - "𝜜-𝜳": # 0x1d71c - 0x1d733 - - tc: "BICGL" + - tc: "BCGL" - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳')" - "𝝰-𝞇": # 0x1d770 - 0x1d787 @@ -167,11 +167,11 @@ - spell: "translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭')" - "𝞪-𝟁": # 0x1d7aa - 0x1d7c1 - - tc: "BISGL" + - tc: "BSGL" - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺𝞻𝞼𝞽𝞾𝞿𝟀𝟁', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺𝞻𝞼𝞽𝞾𝞿𝟀𝟁')" - "𝞐-𝞧": # 0x1d790 - 0x1d7a7 - - tc: "BISCGL" + - tc: "BSCGL" - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧')" - "𝟎-𝟗": # 0x1d7ce - 0x1d7d7 diff --git a/Rules/Braille/CMU/unicode.yaml b/Rules/Braille/CMU/unicode.yaml index b7ce85a1b..fa027b0b6 100644 --- a/Rules/Braille/CMU/unicode.yaml +++ b/Rules/Braille/CMU/unicode.yaml @@ -107,84 +107,17 @@ - "Û": [t: "CL⠾"] # 0xDA - "Ü": [t: "CL⠳"] # 0xDC - - "A": [t: "CL⠁"] # 0x41 - - "B": # 0x42 + # Latin capitals: chem-element letters use 𝐶 (no word indicator); others use C. + # Cells come from lowercase a-z via spell/translate. + - "BCHIKNOPSUVWY": # chemistry-capable capitals - test: if: "@data-chem-element" - then: [t: "𝐶L⠃"] - else: [t: "CL⠃"] - - "C": # 0x43 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠉"] - else: [t: "CL⠉"] - - "D": [t: "CL⠙"] # 0x44 - - "E": [t: "CL⠑"] # 0x45 - - "F": [t: "CL⠋"] # 0x46 - - "G": [t: "CL⠛"] # 0x47 - - "H": # 0x48 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠓"] - else: [t: "CL⠓"] - - "I": # 0x49 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠊"] - else: [t: "CL⠊"] - - "J": [t: "CL⠚"] # 0x4a - - "K": # 0x4b - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠅"] - else: [t: "CL⠅"] - - "L": [t: "CL⠇"] # 0x4c - - "M": [t: "CL⠍"] # 0x4d - - "N": # 0x4e - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠝"] - else: [t: "CL⠝"] - - "O": # 0x4f - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠕"] - else: [t: "CL⠕"] - - "P": # 0x50 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠏"] - else: [t: "CL⠏"] - - "Q": [t: "CL⠟"] # 0x51 - - "R": [t: "CL⠗"] # 0x52 - - "S": # 0x53 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠎"] - else: [t: "CL⠎"] - - "T": [t: "CL⠞"] # 0x54 - - "U": # 0x55 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠥"] - else: [t: "CL⠥"] - - "V": # 0x56 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠧"] - else: [t: "CL⠧"] - - "W": # 0x57 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠺"] - else: [t: "CL⠺"] - - "X": [t: "CL⠭"] # 0x58 - - "Y": # 0x59 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠽"] - else: [t: "CL⠽"] - - "Z": [t: "CL⠵"] # 0x5a + then: [tc: "𝐶"] + else: [tc: "C"] + - spell: "translate('.', 'BCHIKNOPSUVWY', 'bchiknopsuvwy')" + - "ADEFGJLMQRTXZ": # remaining capitals + - tc: "C" + - spell: "translate('.', 'ADEFGJLMQRTXZ', 'adefgjlmqrtxz')" - "α": [t: "GL⠁"] # 0x3b1 - "β": [t: "GL⠃"] # 0x3b2 diff --git a/Rules/Braille/Nemeth/unicode-full.yaml b/Rules/Braille/Nemeth/unicode-full.yaml index 06831aec3..935933a2a 100644 --- a/Rules/Braille/Nemeth/unicode-full.yaml +++ b/Rules/Braille/Nemeth/unicode-full.yaml @@ -1,1375 +1,1364 @@ ---- -# To greatly simplify typeface/language generation, the chars have unique ASCII chars for them: -# Typeface: S: sans-serif, B: bold, T: script/blackboard, I: italic, R: Roman -# Language: E: English, D: German, G: Greek, V: Greek variants, H: Hebrew, U: Russian -# Indicators: C: capital, L: letter, N: number, P: punctuation, M: multipurpose -# Others: W -- whitespace that should be kept (e.g, in a numeral) -# SRE doesn't have H: Hebrew, so not encoded (yet) -# Some of the weird letters (e.g., circled letters) I didn't translate with the above because -# they don't interact with rules since they are enclosed in other chars. - - - "⨯": [t: "⠈⠡"] # U+2A2F(VECTOR OR CROSS PRODUCT) -- make the same as 0x00D7 (Multiplication sign) - - # --- Nemeth Default Alphabet bold chars. --- - - "𝐀-𝐙": # 0x1d400 - 0x1d419 - - tc: "BE" - - spell: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - "𝐚-𝐳": # 0x1d41a - 0x1d433 - - tc: "BE" - - spell: "translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 - - tc: "BG" - - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝛂-𝛒": # 0x1d6c2 - 0x1d6d2 - - tc: "BG" - - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒', 'αβγδεζηθικλμνξοπρ')" - - - "𝛔-𝛖": # 0x1d6d4 - 0x1d6d6 - - tc: "BG" - - spell: "translate('.', '𝛔𝛕𝛖', 'στυ')" - - - "𝛘-𝛚": # 0x1d6d8 - 0x1d6da - - tc: "BG" - - spell: "translate('.', '𝛘𝛙𝛚', 'χψω')" - - - "𝛁": [tc: "BGL⠫"] # 0x1d6c1 - - "𝛓": [tc: "BVL⠎"] # 0x1d6d3 - - "𝛗": [tc: "BVL⠋"] # 0x1d6d7 - - "𝛛": [tc: "⠸⠈⠙"] # 0x1d6db - - "𝛜": [tc: "BGL⠑"] # 0x1d6dc - - "𝛝": [tc: "BVL⠹"] # 0x1d6dd - - "𝛞": [tc: "BGL⠅"] # 0x1d6de - - "𝛟": [tc: "BGL⠋"] # 0x1d6df - - "𝛠": [tc: "BGL⠗"] # 0x1d6e0 - - "𝛡": [tc: "BGL⠏"] # 0x1d6e1 - - - "𝟎-𝟗": # 0x1d7ce - 0x1d7d7 - - tc: "B" - - spell: "translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789')" - - # --- Nemeth Default Alphabet bold-fraktur chars. --- - - - "𝕬-𝖅": # 0x1d56c - 0x1d585 - - tc: "BD" - - spell: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝖆-𝖟": # 0x1d586 - 0x1d59f - - tc: "BD" - - spell: "translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz')" - - # --- Nemeth Default Alphabet bold-italic chars. --- - - "𝑨-𝒁": # 0x1d468 - 0x1d481 - - tc: "BIE" - - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝒂-𝒛": # 0x1d482 - 0x1d49b - - tc: "BIE" - - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝜜-𝜴": # 0x1d71c - 0x1d734 - - tc: "BIG" - - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳𝜴', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝜵": [tc: "BIGL⠫"] # 0x1d735 - - - "𝜶-𝝆": # 0x1d736 - 0x1d746 - - tc: "BIG" - - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆', 'αβγδεζηθικλμνξοπρ')" - - - "𝝇": [tc: "BIVL⠎"] # 0x1d747 - - - "𝝈-𝝊": # 0x1d748 - 0x1d74a - - tc: "BIG" - - spell: "translate('.', '𝝈𝝉𝝊', 'στυ')" - - - "𝝋": [tc: "BIVL⠋"] # 0x1d74b - - - "𝝌-𝝎": # 0x1d74c - 0x1d74e - - tc: "BIG" - - spell: "translate('.', '𝝌𝝍𝝎', 'χψω')" - - - "𝝏": [tc: "BVL⠙"] # 0x1d74f - - "𝝐": [tc: "BIGL⠑"] # 0x1d750 - - "𝝑": [tc: "BIVL⠹"] # 0x1d751 - - - "𝝒-𝝕": # 0x1d752 - 0x1d755 - - tc: "BIG" - - spell: "translate('.', '𝝒𝝓𝝔𝝕', 'κφρπ')" - - # --- Nemeth Default Alphabet bold-script chars. --- - - - "𝓐-𝓩": # 0x1d4d0 - 0x1d4e9 - - tc: "BTE" - - spell: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝓪-𝔃": # 0x1d4ea - 0x1d503 - - tc: "BTE" - - spell: "translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝚤-𝚥": # 0x1d6a4 - 0x1d6a5 - - tc: "" - - spell: "translate('.', '𝚤𝚥', '⠊⠚')" - -# Russian Alphabet (from Nemeth book) -# NOTE: is this translation needed? - - "А-Я": # 0x410 - 0x42f - - tc: "U" - - spell: "translate('.', 'АБВГДЕЗИЙКЛМНОПРСТУФЭ', 'ABVGDEZIYKLMNOPRSTUFE')" - - - "а-я": # 0x430 - 0x44f - - tc: "U" - - spell: "translate('.', 'абвгдезийклмнопрстуфэ', 'abvgdeziylkmnoprstufye')" - - - "①-⑨": # 0x2460 - 0x2473 - - tc: "⠫⠉⠸⠫⠼" - - spell: "translate('.', '①②③④⑤⑥⑦⑧⑨', '123456789')" - - tc: "⠻" - - - "⓪": [tc: "⠫⠉⠸⠫⠼⠴⠻"] # 0x24ea - - - "⑩": [tc: "⠫⠉⠸⠫⠼⠂⠴⠻"] # 0x2469 - - "⑪": [tc: "⠫⠉⠸⠫⠼⠂⠂⠻"] # 0x246a - - "⑫": [tc: "⠫⠉⠸⠫⠼⠂⠆⠻"] # 0x246b - - "⑬": [tc: "⠫⠉⠸⠫⠼⠂⠒⠻"] # 0x246c - - "⑭": [tc: "⠫⠉⠸⠫⠼⠂⠲⠻"] # 0x246d - - "⑮": [tc: "⠫⠉⠸⠫⠼⠂⠢⠻"] # 0x246e - - "⑯": [tc: "⠫⠉⠸⠫⠼⠂⠖⠻"] # 0x246f - - "⑰": [tc: "⠫⠉⠸⠫⠼⠂⠶⠻"] # 0x2470 - - "⑱": [tc: "⠫⠉⠸⠫⠼⠂⠦⠻"] # 0x2471 - - "⑲": [tc: "⠫⠉⠸⠫⠼⠂⠔⠻"] # 0x2472 - - "⑳": [tc: "⠫⠉⠸⠫⠼⠆⠴⠻"] # 0x2473 - - - "㉑": [tc: "⠫⠉⠸⠫⠼⠆⠂⠻"] # 0x3251 - - "㉒": [tc: "⠫⠉⠸⠫⠼⠆⠆⠻"] # 0x3252 - - "㉓": [tc: "⠫⠉⠸⠫⠼⠆⠒⠻"] # 0x3253 - - "㉔": [tc: "⠫⠉⠸⠫⠼⠆⠲⠻"] # 0x3254 - - "㉕": [tc: "⠫⠉⠸⠫⠼⠆⠢⠻"] # 0x3255 - - "㉖": [tc: "⠫⠉⠸⠫⠼⠆⠖⠻"] # 0x3256 - - "㉗": [tc: "⠫⠉⠸⠫⠼⠆⠶⠻"] # 0x3257 - - "㉘": [tc: "⠫⠉⠸⠫⠼⠆⠦⠻"] # 0x3258 - - "㉙": [tc: "⠫⠉⠸⠫⠼⠆⠔⠻"] # 0x3259 - - "㉚": [tc: "⠫⠉⠸⠫⠼⠒⠴⠻"] # 0x325a - - "㉛": [tc: "⠫⠉⠸⠫⠼⠒⠂⠻"] # 0x325b - - "㉜": [tc: "⠫⠉⠸⠫⠼⠒⠆⠻"] # 0x325c - - "㉝": [tc: "⠫⠉⠸⠫⠼⠒⠒⠻"] # 0x325d - - "㉞": [tc: "⠫⠉⠸⠫⠼⠒⠲⠻"] # 0x325e - - "㉟": [tc: "⠫⠉⠸⠫⠼⠒⠢⠻"] # 0x325f - - "㊱": [tc: "⠫⠉⠸⠫⠼⠒⠖⠻"] # 0x32b1 - - "㊲": [tc: "⠫⠉⠸⠫⠼⠒⠶⠻"] # 0x32b2 - - "㊳": [tc: "⠫⠉⠸⠫⠼⠒⠦⠻"] # 0x32b3 - - "㊴": [tc: "⠫⠉⠸⠫⠼⠒⠔⠻"] # 0x32b4 - - "㊵": [tc: "⠫⠉⠸⠫⠼⠲⠴⠻"] # 0x32b5 - - "㊶": [tc: "⠫⠉⠸⠫⠼⠲⠂⠻"] # 0x32b6 - - "㊷": [tc: "⠫⠉⠸⠫⠼⠲⠆⠻"] # 0x32b7 - - "㊸": [tc: "⠫⠉⠸⠫⠼⠲⠒⠻"] # 0x32b8 - - "㊹": [tc: "⠫⠉⠸⠫⠼⠲⠲⠻"] # 0x32b9 - - "㊺": [tc: "⠫⠉⠸⠫⠼⠲⠢⠻"] # 0x32ba - - "㊻": [tc: "⠫⠉⠸⠫⠼⠲⠖⠻"] # 0x32bb - - "㊼": [tc: "⠫⠉⠸⠫⠼⠲⠶⠻"] # 0x32bc - - "㊽": [tc: "⠫⠉⠸⠫⠼⠲⠦⠻"] # 0x32bd - - "㊾": [tc: "⠫⠉⠸⠫⠼⠲⠔⠻"] # 0x32be - - "㊿": [tc: "⠫⠉⠸⠫⠼⠢⠴⠻"] # 0x32bf - -#NOTE: uncertainty here - - "Ⓐ-Ⓩ": # 0x24b6 - 0x24cf - - tc: "⠫⠉⠸⠫⠠" - - spell: "translate('.', 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - tc: "⠻" - - - "ⓐ-ⓩ": # 0x24d0 - 0x24e9 - - tc: "⠫⠉⠸⠫" - - spell: "translate('.', 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ', 'abcdefghijklmnopqrstuvwxyz')" - - tc: "⠻" - - # --- Nemeth Default Alphabet circled-sans-serif chars. --- - - - "➀-➈": # 0x1f10b, 0x2780 - 0x2788 - - tc: "⠫⠉⠸⠫⠠⠨⠼" - - spell: "translate('.', '➀➁➂➃➄➅➆➇➈', '123456789')" - - tc: "⠻" - - - "🄋": [tc: "⠫⠉⠸⠫⠠⠨⠼⠴⠻"] # 0x1f10b - - "➉": [tc: "⠫⠉⠸⠫⠠⠨⠼⠂⠴⠻"] # 0x2789 - - # --- Nemeth Default Alphabet comma chars. --- - - - "🄁-🄊": # 0x1f101 - 0x1f10a - - spell: "translate('.', '🄁🄂🄃🄄🄅🄆🄇🄈🄉🄊', '0123456789')" - - tc: "⠠" - - - # --- Nemeth Default Alphabet double-circled chars. --- - - - "⓵": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠂⠻⠻"] # 0x24f5 - - "⓶": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠆⠻⠻"] # 0x24f6 - - "⓷": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠒⠻⠻"] # 0x24f7 - - "⓸": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠲⠻⠻"] # 0x24f8 - - "⓹": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠢⠻⠻"] # 0x24f9 - - "⓺": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠖⠻⠻"] # 0x24fa - - "⓻": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠶⠻⠻"] # 0x24fb - - "⓼": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠦⠻⠻"] # 0x24fc - - "⓽": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠔⠻⠻"] # 0x24fd - - "⓾": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠂⠴⠻⠻"] # 0x24fe - - # --- Nemeth Default Alphabet double-struck chars. --- - - "𝔸-𝕐": # 0x1d538 - 0x1d550 (partial, excludes ℂ ℍ ℕ ℙ ℚ ℝ ℤ) - - tc: "𝔹E" - - spell: "translate('.', '𝔸𝔹𝔻𝔼𝔽𝔾𝕀𝕁𝕂𝕃𝕄𝕆𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'ABDEFGIJKLMOSTUVWXY')" - - - "ℂ": [tc: "𝔹ECL⠉"] # 0x2102 - - "ℍ": [tc: "𝔹ECL⠓"] # 0x210d - - "ℕ": [tc: "𝔹ECL⠝"] # 0x2115 - - "ℙ": [tc: "𝔹ECL⠏"] # 0x2119 - - "ℚ": [tc: "𝔹ECL⠟"] # 0x211a - - "ℝ": [tc: "𝔹ECL⠗"] # 0x211d - - "ℤ": [tc: "𝔹ECL⠵"] # 0x2124 - - "Ω": [t: "CGL⠺"] # 0x2126 (Ohm sign (capital Greek omega)) - - - "𝕒-𝕫": # 0x1d552 - 0x1d56b - - tc: "𝔹E" - - spell: "translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝟘-𝟡": # 0x1d7d8 - 0x1d7e1 - - tc: "𝔹" - - spell: "translate('.', '𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡', '0123456789')" - - - # --- Nemeth Default Alphabet fraktur chars. --- - - - "𝔄-𝔜": # 0x1d504 - 0x1d51c - - tc: "D" - - spell: "translate('.', '𝔄𝔅𝔇𝔈𝔉𝔊𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔖𝔗𝔘𝔙𝔚𝔛𝔜', 'ABDEFGJKLMNOPQSTUVWXY')" - - - "ℭ": [tc: "DCL⠉"] # 0x212d - - "ℌ": [tc: "DCL⠓"] # 0x210c - - "ℑ": [tc: "DCL⠊"] # 0x2111 - - "ℜ": [tc: "DCL⠗"] # 0x211c - - "ℨ": [tc: "DCL⠵"] # 0x2128 - - - "𝔞-𝔷": # 0x1d51e - 0x1d537 - - tc: "D" - - spell: "translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz')" - - # --- Nemeth Default Alphabet fullwidth chars. --- - - - "A-Z": # 0xff21 - 0xff3a - - tc: "" - - spell: "translate('.', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "a-z": # 0xff41 - 0xff5a - - tc: "" - - spell: "translate('.', 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz')" - - - "0-9": # 0xff10 - 0xff19 - - tc: "" - - spell: "translate('.', '0123456789', '0123456789')" - - # --- Nemeth Default Alphabet italic chars -- treat as non-italic. --- - - - "𝐴-𝑍": # 0x1d434 - 0x1d44d - - tc: "E" - - spell: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝑎-𝑧": # 0x1d44e - 0x1d467 - - tc: "E" - - spell: "translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔ℎ𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝛢-𝛺": # 0x1d6e2 - 0x1d6fa - - tc: "IG" - - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹𝛺', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝛻": [tc: "IGL⠫"] # 0x1d6fb - - - "𝛼-𝜌": # 0x1d6fc - 0x1d70c - - tc: "IG" - - spell: "translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌', 'αβγδεζηθικλμνξοπρ')" - - "𝜍": [tc: "IVL⠎"] # 0x1d70d - - - "𝜎": [tc: "IGL⠎"] # 0x1d70e - - "𝜏": [tc: "IGL⠞"] # 0x1d70f - - "𝜐": [tc: "IGL⠥"] # 0x1d710 - - - "𝜑": [tc: "IVL⠋"] # 0x1d711 - - - "𝜒": [tc: "IGL⠯"] # 0x1d712 - - "𝜓": [tc: "IGL⠽"] # 0x1d713 - - "𝜔": [tc: "IGL⠺"] # 0x1d714 - - - "𝜕": [tc: "VL⠙"] # 0x1d715 - - - "𝜖": [tc: "IGL⠑"] # 0x1d716 - - - "𝜗": [tc: "IVL⠹"] # 0x1d717 - - - "𝜘": [tc: "IGL⠅"] # 0x1d718 - - "𝜙": [tc: "IGL⠋"] # 0x1d719 - - "𝜚": [tc: "IGL⠗"] # 0x1d71a - - "𝜛": [tc: "IGL⠏"] # 0x1d71b - - # --- Nemeth Default Alphabet monospace chars. --- - - - "𝙰-𝚉": # 0x1d670 - 0x1d689 - - tc: "" - - spell: "translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝚊-𝚣": # 0x1d68a - 0x1d6a3 - - tc: "" - - spell: "translate('.', '𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝟶-𝟿": # 0x1d7f6 - 0x1d7ff - - tc: "" - - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" - - - # --- Nemeth Default Alphabet negative-circled chars. --- - - "⓿": [t: "⠫⠸⠉⠸⠫⠼⠴⠻"] # 0x24ff - - "❶": [t: "⠫⠸⠉⠸⠫⠼⠂⠻"] # 0x2776 - - "❷": [t: "⠫⠸⠉⠸⠫⠼⠆⠻"] # 0x2777 - - "❸": [t: "⠫⠸⠉⠸⠫⠼⠒⠻"] # 0x2778 - - "❹": [t: "⠫⠸⠉⠸⠫⠼⠲⠻"] # 0x2779 - - "❺": [t: "⠫⠸⠉⠸⠫⠼⠢⠻"] # 0x277a - - "❻": [t: "⠫⠸⠉⠸⠫⠼⠖⠻"] # 0x277b - - "❼": [t: "⠫⠸⠉⠸⠫⠼⠶⠻"] # 0x277c - - "❽": [t: "⠫⠸⠉⠸⠫⠼⠦⠻"] # 0x277d - - "❾": [t: "⠫⠸⠉⠸⠫⠼⠔⠻"] # 0x277e - - "❿": [t: "⠫⠸⠉⠸⠫⠼⠂⠴⠻"] # 0x277f - - "⓫": [t: "⠫⠸⠉⠸⠫⠼⠂⠂⠻"] # 0x24eb - - "⓬": [t: "⠫⠸⠉⠸⠫⠼⠂⠆⠻"] # 0x24ec - - "⓭": [t: "⠫⠸⠉⠸⠫⠼⠂⠒⠻"] # 0x24ed - - "⓮": [t: "⠫⠸⠉⠸⠫⠼⠂⠲⠻"] # 0x24ee - - "⓯": [t: "⠫⠸⠉⠸⠫⠼⠂⠢⠻"] # 0x24ef - - "⓰": [t: "⠫⠸⠉⠸⠫⠼⠂⠖⠻"] # 0x24f0 - - "⓱": [t: "⠫⠸⠉⠸⠫⠼⠂⠶⠻"] # 0x24f1 - - "⓲": [t: "⠫⠸⠉⠸⠫⠼⠂⠦⠻"] # 0x24f2 - - "⓳": [t: "⠫⠸⠉⠸⠫⠼⠂⠔⠻"] # 0x24f3 - - "⓴": [t: "⠫⠸⠉⠸⠫⠼⠆⠴⠻"] # 0x24f4 - - - "🅐": [t: "⠫⠸⠉⠸⠫⠠⠁⠻"] # 0x1f150 - - "🅑": [t: "⠫⠸⠉⠸⠫⠠⠃⠻"] # 0x1f151 - - "🅒": [t: "⠫⠸⠉⠸⠫⠠⠉⠻"] # 0x1f152 - - "🅓": [t: "⠫⠸⠉⠸⠫⠠⠙⠻"] # 0x1f153 - - "🅔": [t: "⠫⠸⠉⠸⠫⠠⠑⠻"] # 0x1f154 - - "🅕": [t: "⠫⠸⠉⠸⠫⠠⠋⠻"] # 0x1f155 - - "🅖": [t: "⠫⠸⠉⠸⠫⠠⠛⠻"] # 0x1f156 - - "🅗": [t: "⠫⠸⠉⠸⠫⠠⠓⠻"] # 0x1f157 - - "🅘": [t: "⠫⠸⠉⠸⠫⠠⠊⠻"] # 0x1f158 - - "🅙": [t: "⠫⠸⠉⠸⠫⠠⠚⠻"] # 0x1f159 - - "🅚": [t: "⠫⠸⠉⠸⠫⠠⠅⠻"] # 0x1f15a - - "🅛": [t: "⠫⠸⠉⠸⠫⠠⠇⠻"] # 0x1f15b - - "🅜": [t: "⠫⠸⠉⠸⠫⠠⠍⠻"] # 0x1f15c - - "🅝": [t: "⠫⠸⠉⠸⠫⠠⠝⠻"] # 0x1f15d - - "🅞": [t: "⠫⠸⠉⠸⠫⠠⠕⠻"] # 0x1f15e - - "🅟": [t: "⠫⠸⠉⠸⠫⠠⠏⠻"] # 0x1f15f - - "🅠": [t: "⠫⠸⠉⠸⠫⠠⠟⠻"] # 0x1f160 - - "🅡": [t: "⠫⠸⠉⠸⠫⠠⠗⠻"] # 0x1f161 - - "🅢": [t: "⠫⠸⠉⠸⠫⠠⠎⠻"] # 0x1f162 - - "🅣": [t: "⠫⠸⠉⠸⠫⠠⠞⠻"] # 0x1f163 - - "🅤": [t: "⠫⠸⠉⠸⠫⠠⠥⠻"] # 0x1f164 - - "🅥": [t: "⠫⠸⠉⠸⠫⠠⠧⠻"] # 0x1f165 - - "🅦": [t: "⠫⠸⠉⠸⠫⠠⠺⠻"] # 0x1f166 - - "🅧": [t: "⠫⠸⠉⠸⠫⠠⠭⠻"] # 0x1f167 - - "🅨": [t: "⠫⠸⠉⠸⠫⠠⠽⠻"] # 0x1f168 - - "🅩": [t: "⠫⠸⠉⠸⠫⠠⠵⠻"] # 0x1f169 - - # --- Nemeth Default Alphabet negative-circled-sans-serif chars. --- - - "🄌": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠴⠻"] # 0x1f10c - - "➊": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠂⠻"] # 0x278a - - "➋": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠆⠻"] # 0x278b - - "➌": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠒⠻"] # 0x278c - - "➍": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠲⠻"] # 0x278d - - "➎": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠢⠻"] # 0x278e - - "➏": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠖⠻"] # 0x278f - - "➐": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠶⠻"] # 0x2790 - - "➑": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠦⠻"] # 0x2791 - - "➒": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠔⠻"] # 0x2792 - - "➓": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠂⠴⠻"] # 0x2793 - - # --- Nemeth Default Alphabet negative-squared chars. --- - - "🅰-🆉": # 0x1f170 - 0x1f189 - - tc: "⠫⠸⠲⠸⠫⠠" - - spell: "translate('.', '🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - tc: "⠻" - - # --- Nemeth Default Alphabet parenthesized chars. --- - - - "⑴-⑼": # 0x2474 - 0x247c - - tc: "⠷" - - spell: "translate('.', '⑴⑵⑶⑷⑸⑹⑺⑻⑼', '123456789')" - - tc: "⠾" - - - "⑽": [t: "⠷⠂⠴⠾"] # 0x247d - - "⑾": [t: "⠷⠂⠂⠾"] # 0x247e - - "⑿": [t: "⠷⠂⠆⠾"] # 0x247f - - "⒀": [t: "⠷⠂⠒⠾"] # 0x2480 - - "⒁": [t: "⠷⠂⠲⠾"] # 0x2481 - - "⒂": [t: "⠷⠂⠢⠾"] # 0x2482 - - "⒃": [t: "⠷⠂⠖⠾"] # 0x2483 - - "⒄": [t: "⠷⠂⠶⠾"] # 0x2484 - - "⒅": [t: "⠷⠂⠦⠾"] # 0x2485 - - "⒆": [t: "⠷⠂⠔⠾"] # 0x2486 - - "⒇": [t: "⠷⠆⠴⠾"] # 0x2487 - - - "🄐-🄩": # 0x1f110 - 0x1f129 - - tc: "⠷⠠" - - spell: "translate('.', '🄐🄑🄒🄓🄔🄕🄖🄗🄘🄙🄚🄛🄜🄝🄞🄟🄠🄡🄢🄣🄤🄥🄦🄧🄨🄩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - tc: "⠾" - - - "⒜-⒵": # 0x249c - 0x24b5 - - tc: "⠷" - - spell: "translate('.', '⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵', 'abcdefghijklmnopqrstuvwxyz')" - - tc: "⠾" - - # --- Nemeth Default Alphabet period chars. --- - - "🄀": [t: "⠴⠸⠲"] # 0x1f100 - - "⒈": [t: "⠂⠸⠲"] # 0x2488 - - "⒉": [t: "⠆⠸⠲"] # 0x2489 - - "⒊": [t: "⠒⠸⠲"] # 0x248a - - "⒋": [t: "⠲⠸⠲"] # 0x248b - - "⒌": [t: "⠢⠸⠲"] # 0x248c - - "⒍": [t: "⠖⠸⠲"] # 0x248d - - "⒎": [t: "⠶⠸⠲"] # 0x248e - - "⒏": [t: "⠦⠸⠲"] # 0x248f - - "⒐": [t: "⠔⠸⠲"] # 0x2490 - - - "⒑": [t: "⠂⠴⠸⠲"] # 0x2491 - - "⒒": [t: "⠂⠂⠸⠲"] # 0x2492 - - "⒓": [t: "⠂⠆⠸⠲"] # 0x2493 - - "⒔": [t: "⠂⠒⠸⠲"] # 0x2494 - - "⒕": [t: "⠂⠲⠸⠲"] # 0x2495 - - "⒖": [t: "⠂⠢⠸⠲"] # 0x2496 - - "⒗": [t: "⠂⠖⠸⠲"] # 0x2497 - - "⒘": [t: "⠂⠶⠸⠲"] # 0x2498 - - "⒙": [t: "⠂⠦⠸⠲"] # 0x2499 - - "⒚": [t: "⠂⠔⠸⠲"] # 0x249a - - "⒛": [t: "⠆⠴⠸⠲"] # 0x249b - - # --- Nemeth Default Alphabet sans-serif chars. --- - - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 - - tc: "SE" - - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝖺-𝗓": # 0x1d5ba - 0x1d5d3 - - tc: "SE" - - spell: "translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝟢-𝟫": # 0x1d7e2 - 0x1d7eb - - tc: "S" - - spell: "translate('.', '𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫', '0123456789')" - - # --- Nemeth Default Alphabet sans-serif-bold chars. --- - - "𝗔-𝗭": # 0x1d5d4 - 0x1d5ed - - tc: "SBE" - - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝗮-𝘇": # 0x1d5ee - 0x1d607 - - tc: "SBE" - - spell: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝝖-𝝮": # 0x1d756 - 0x1d76e - - tc: "SBG" - - spell: "translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭𝝮', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝝰-𝞀": # 0x1d770 - 0x1d780 - - tc: "SBG" - - spell: "translate('.', '𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿𝞀', 'αβγδεζηθικλμνξοπρ')" - - - "𝝯": [tc: "SBGL⠫"] # 0x1d76f - - - "𝞁": [tc: "SBVL⠎"] # 0x1d781 - - - "𝞂-𝞄": # 0x1d782 - 0x1d784 - - tc: "SBG" - - spell: "translate('.', '𝞂𝞃𝞄', 'στυ')" - - - "𝞅": [tc: "SBVL⠋"] # 0x1d785 - - - "𝞆-𝞈": # 0x1d786 - 0x1d788 - - tc: "SBG" - - spell: "translate('.', '𝞆𝞇𝞈', 'χψω')" - - - "𝞉": [tc: "⠠⠨⠸⠈⠙"] # 0x1d789 - - "𝞊": [tc: "SBGL⠑"] # 0x1d78a - - "𝞋": [tc: "SBVL⠹"] # 0x1d78b - - - "𝞌-𝞏": # 0x1d78c - 0x1d78f - - tc: "SBG" - - spell: "translate('.', '𝞌𝞍𝞎𝞏', 'κφρπ')" - - - "𝟬-𝟵": # 0x1d7ec - 0x1d7f5 - - tc: "SB" - - spell: "translate('.', '𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵', '0123456789')" - - # --- Nemeth Default Alphabet sans-serif-bold-italic chars. --- - - - "𝘼-𝙕": # 0x1d63c - 0x1d655 - - tc: "SBIE" - - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝙖-𝙯": # 0x1d656 - 0x1d66f - - tc: "SBIE" - - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝞐-𝞨": # 0x1d790 - 0x1d7a8 - - tc: "SBIG" - - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝞩": [t: "SBIGL⠫"] # 0x1d7a9 - - - "𝞪-𝞺": # 0x1d7aa - 0x1d7ba - - tc: "SBIG" - - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺', 'αβγδεζηθικλμνξοπρ')" - - - "𝞻": [t: "SBIVL⠎"] # 0x1d7bb - - "𝞼": [t: "SBIGL⠎"] # 0x1d7bc - - "𝞽": [t: "SBIGL⠞"] # 0x1d7bd - - "𝞾": [t: "SBIGL⠥"] # 0x1d7be - - "𝞿": [t: "SBIVL⠋"] # 0x1d7bf - - - "𝟀": [t: "SBIGL⠯"] # 0x1d7c0 - - "𝟁": [t: "SBIGL⠽"] # 0x1d7c1 - - "𝟂": [t: "SBIGL⠺"] # 0x1d7c2 - - "𝟃": [t: "SBVL⠙"] # 0x1d7c3 - - - "𝟄": [t: "SBIGL⠑"] # 0x1d7c4 - - "𝟅": [t: "SBIVL⠹"] # 0x1d7c5 - - - "𝟆": [t: "SBIGL⠅"] # 0x1d7c6 - - "𝟇": [t: "SBIGL⠋"] # 0x1d7c7 - - "𝟈": [t: "SBIGL⠗"] # 0x1d7c8 - - "𝟉": [t: "SBIGL⠏"] # 0x1d7c9 - - # --- Nemeth Default Alphabet sans-serif-italic chars. --- - - "𝘈-𝘡": # 0x1d608 - 0x1d621 - - tc: "SIE" - - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝘢-𝘻": # 0x1d622 - 0x1d63b - - tc: "SIE" - - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" - - # --- Nemeth Default Alphabet script chars. --- - - "𝒜-𝒵": # 0x1d49c - 0x1d4b5 - - tc: "TE" - - spell: "translate('.', '𝒜𝒞𝒟𝒢𝒥𝒦𝒩𝒪𝒫𝒬𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'ACDGJKNOPQSTUVWXYZ')" - - - "ℬ": [t: "TECL⠃"] # 0x212c - - "ℰ": [t: "TECL⠑"] # 0x2130 - - "ℱ": [t: "TECL⠋"] # 0x2131 - - "ℋ": [t: "TECL⠓"] # 0x210b - - "ℐ": [t: "TECL⠊"] # 0x2110 - - "ℒ": [t: "TECL⠇"] # 0x2112 - - "ℳ": [t: "TECL⠍"] # 0x2133 - - "ℛ": [t: "TECL⠗"] # 0x211b - - - "𝒶-𝓏": # 0x1d4b6 - 0x1d4cf - - tc: "TE" - - spell: "translate('.', '𝒶𝒷𝒸𝒹𝒻𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏', 'abcdfhijklmnpqrstuvwxyz')" - - - "ℯ": [t: "TEL⠑"] # 0x212f - - "ℊ": [t: "TEL⠛"] # 0x210a - - "ℴ": [t: "TEL⠕"] # 0x2134 - - # --- Nemeth Default Alphabet squared chars. --- - - - "🄰": [t: "⠫⠲⠸⠫⠠⠁⠻"] # 0x1f130 - - "🄱": [t: "⠫⠲⠸⠫⠠⠃⠻"] # 0x1f131 - - "🄲": [t: "⠫⠲⠸⠫⠠⠉⠻"] # 0x1f132 - - "🄳": [t: "⠫⠲⠸⠫⠠⠙⠻"] # 0x1f133 - - "🄴": [t: "⠫⠲⠸⠫⠠⠑⠻"] # 0x1f134 - - "🄵": [t: "⠫⠲⠸⠫⠠⠋⠻"] # 0x1f135 - - "🄶": [t: "⠫⠲⠸⠫⠠⠛⠻"] # 0x1f136 - - "🄷": [t: "⠫⠲⠸⠫⠠⠓⠻"] # 0x1f137 - - "🄸": [t: "⠫⠲⠸⠫⠠⠊⠻"] # 0x1f138 - - "🄹": [t: "⠫⠲⠸⠫⠠⠚⠻"] # 0x1f139 - - "🄺": [t: "⠫⠲⠸⠫⠠⠅⠻"] # 0x1f13a - - "🄻": [t: "⠫⠲⠸⠫⠠⠇⠻"] # 0x1f13b - - "🄼": [t: "⠫⠲⠸⠫⠠⠍⠻"] # 0x1f13c - - "🄽": [t: "⠫⠲⠸⠫⠠⠝⠻"] # 0x1f13d - - "🄾": [t: "⠫⠲⠸⠫⠠⠕⠻"] # 0x1f13e - - "🄿": [t: "⠫⠲⠸⠫⠠⠏⠻"] # 0x1f13f - - "🅀": [t: "⠫⠲⠸⠫⠠⠟⠻"] # 0x1f140 - - "🅁": [t: "⠫⠲⠸⠫⠠⠗⠻"] # 0x1f141 - - "🅂": [t: "⠫⠲⠸⠫⠠⠎⠻"] # 0x1f142 - - "🅃": [t: "⠫⠲⠸⠫⠠⠞⠻"] # 0x1f143 - - "🅄": [t: "⠫⠲⠸⠫⠠⠥⠻"] # 0x1f144 - - "🅅": [t: "⠫⠲⠸⠫⠠⠧⠻"] # 0x1f145 - - "🅆": [t: "⠫⠲⠸⠫⠠⠺⠻"] # 0x1f146 - - "🅇": [t: "⠫⠲⠸⠫⠠⠭⠻"] # 0x1f147 - - "🅈": [t: "⠫⠲⠸⠫⠠⠽⠻"] # 0x1f148 - - "🅉": [t: "⠫⠲⠸⠫⠠⠵⠻"] # 0x1f149 - - # --- Nemeth Default Alphabet sub chars. --- - - - "₀": [t: "⠰⠴"] # 0x2080 - - "₁": [t: "⠰⠂"] # 0x2081 - - "₂": [t: "⠰⠆"] # 0x2082 - - "₃": [t: "⠰⠒"] # 0x2083 - - "₄": [t: "⠰⠲"] # 0x2084 - - "₅": [t: "⠰⠢"] # 0x2085 - - "₆": [t: "⠰⠖"] # 0x2086 - - "₇": [t: "⠰⠶"] # 0x2087 - - "₈": [t: "⠰⠦"] # 0x2088 - - "₉": [t: "⠰⠔"] # 0x2089 - - # --- Nemeth Default Alphabet super chars. --- - - - "⁰": [t: "⠘⠴"] # 0x2070 - - "¹": [t: "⠘⠂"] # 0xb9 - - "²": [t: "⠘⠆"] # 0xb2 - - "³": [t: "⠘⠒"] # 0xb3 - - "⁴": [t: "⠘⠲"] # 0x2074 - - "⁵": [t: "⠘⠢"] # 0x2075 - - "⁶": [t: "⠘⠖"] # 0x2076 - - "⁷": [t: "⠘⠶"] # 0x2077 - - "⁸": [t: "⠘⠦"] # 0x2078 - - "⁹": [t: "⠘⠔"] # 0x2079 - - # --- Nemeth Default Characters latin-lower-phonetic chars. --- - - - "ø": [t: "⠈⠕"] # 0xf8 - - # --- Nemeth Default Characters math_geometry chars. --- - - - "─": [t: "⠱"] # 0x2500 - - "│": [t: "⠳"] # 0x2502 - - "▰": [t: "⠫⠸⠛"] # 0x25b0 - - "▱": [t: "⠫⠛"] # 0x25b1 - - "▽": [t: "⠨⠫"] # 0x25bd - - "◆": [t: "⠫⠸⠙"] # 0x25c6 - - "◇": [t: "⠫⠙"] # 0x25c7 - - "◈": [t: "⠫⠸⠙"] # 0x25c8 - - "◉": [t: "⠫⠸⠉"] # 0x25c9 - - "◊": [t: "⠫⠙"] # 0x25ca - - "⬟": [t: "⠫⠸⠢"] # 0x2b1f - - "⬠": [t: "⠫⠢"] # 0x2b20 - - "⬡": [t: "⠫⠖"] # 0x2b21 - - "⬢": [t: "⠫⠸⠖"] # 0x2b22 - - "⬣": [t: "⠫⠸⠖"] # 0x2b23 - - "⬤": [t: "⠫⠸⠉"] # 0x2b24 - - "⬥": [t: "⠫⠸⠙"] # 0x2b25 - - # --- Nemeth Default Characters math_symbols chars. --- - - - "\"": [t: "⠄⠄"] # 0x22 - - "¡": [t: "⠖"] # 0xa1 - - "¢": [t: "⠈⠉"] # 0xa2 - - "£": [t: "⠈⠇"] # 0xa3 - - "¤": [t: "⠈⠫"] # 0xa4 - - "¥": [t: "⠈⠽"] # 0xa5 - - "§": [t: "⠈⠠⠎"] # 0xa7 - - "©": [t: "⠘⠉"] # 0xa9 (Copyright) - - "ª": [t: "⠸⠰⠁"] # 0xaa - - "«": [t: "⠐⠅⠈⠐⠅⠻"] # 0xab - - "®": [t: "⠘⠗"] # 0xae - - "´": [t: "⠈"] # 0xb4 - - "µ": [t: "⠨⠍"] # 0xb5 - - "¶": [t: "⠈⠠⠏"] # 0xb6 - - "¿": [t: "⠦"] # 0xbf - - "ʹ": [t: "⠄"] # 0x2b9 - - "ʺ": [t: "⠄⠄"] # 0x2ba - - "˙": [t: "⠡"] # 0x2d9 - - "˜": [t: "⠈⠱"] # 0x2dc - - "‐": [t: "⠤"] # 0x2010 - - "‑": [t: "⠤"] # 0x2011 - - "‒": [t: "⠤⠤"] # 0x2012 - - "–": [t: "⠤⠤"] # 0x2013 - - "‖": [t: "⠳⠳"] # 0x2016 - - "†": [t: "⠸⠻"] # 0x2020 - - "‡": [t: "⠸⠸⠻"] # 0x2021 - - "•": [t: "⠡"] # 0x2022 - - "․": [t: "⠄"] # 0x2024 - - "‥": [t: "⠄⠄"] # 0x2025 - - "′": [t: "⠄"] # 0x2032 - - "″": [t: "⠄⠄"] # 0x2033 - - "‴": [t: "⠄⠄."] # 0x2034 (Triple prime) - - "⁗": [t: "⠄⠄.."] # 0x2057 (Quadruple prime) - - "‼": [t: "⠖⠖"] # 0x203c - - "‾": [t: "⠱"] # 0x203e - - "⁇": [t: "⠹⠹"] # 0x2047 - - "⁈": [t: "⠹⠖"] # 0x2048 - - "⁉": [t: "⠖⠹"] # 0x2049 - - "∀": [t: "⠈⠯"] # 0x2200 - - "∃": [t: "⠈⠿"] # 0x2203 - - "∄": [t: "⠌⠈⠿"] # 0x2204 - - "∅": [t: "⠸⠴"] # 0x2205 - - "∈": [t: "⠈⠑"] # 0x2208 - - "∉": [t: "⠌⠈⠑"] # 0x2209 - - "∊": [t: "⠈⠑"] # 0x220a - - "∋": [t: "⠈⠢"] # 0x220b - - "∌": [t: "⠌⠈⠢"] # 0x220c - - "∍": [t: "⠈⠢"] # 0x220d - - "∎": [t: "⠸⠳"] # 0x220e - - "−": [t: "⠤"] # 0x2212 - - "∓": [t: "⠤⠬"] # 0x2213 - - "∕": [t: "⠸⠌"] # 0x2215 - - "∖": [t: "⠸⠡"] # 0x2216 - - "∗": [t: "⠈⠼"] # 0x2217 - - "∘": [t: "⠨⠡"] # 0x2218 - - "∝": [t: "⠸⠿"] # 0x221d - - "∞": [t: "⠠⠿"] # 0x221e - - "∟": [t: "⠫⠪⠨⠗⠻"] # 0x221f - - "∠": [t: "⠫⠪"] # 0x2220 - - "∢": [t: "⠫⠪⠸⠫⠫⠁⠻"] # 0x2222 - - "∣": [t: "⠳"] # 0x2223 - - "∤": [t: "⠌⠳"] # 0x2224 - - "∥": [t: "⠫⠇"] # 0x2225 - - "∦": [t: "⠌⠫⠇"] # 0x2226 - - "∧": [t: "⠈⠩"] # 0x2227 - - "∨": [t: "⠈⠬"] # 0x2228 - - "∩": [t: "⠨⠩"] # 0x2229 - - "∪": [t: "⠨⠬"] # 0x222a - - "∫": [t: "⠮"] # 0x222b - - "∬": [t: "⠮⠮"] # 0x222c - - "∭": [t: "⠮⠮⠮"] # 0x222d - - "∮": [t: "⠮⠈⠫⠉⠻"] # 0x222e - - "∲": [t: "⠮⠈⠫⠪⠢⠔⠻"] # 0x2232 - - "∳": [t: "⠮⠈⠫⠢⠔⠕⠻"] # 0x2233 - - "∴": [t: "⠠⠡"] # 0x2234 - - "∵": [t: "⠈⠌"] # 0x2235 - - "∶": [t: "⠐⠂"] # 0x2236 - - "∷": [t: "⠰⠆"] # 0x2237 - - "∸": [t: "⠨⠤"] # 0x2238 - - "∼": [t: "⠈⠱"] # 0x223c - - "≁": [t: "⠌⠈⠱"] # 0x2241 - - "≂": [t: "⠱⠈⠱"] # 0x2242 - - "≃": [t: "⠈⠱⠱"] # 0x2243 - - "≄": [t: "⠌⠈⠱⠱"] # 0x2244 - - "≅": [t: "⠈⠱⠨⠅"] # 0x2245 - - "≆": [t: "⠈⠱⠌⠨⠅"] # 0x2246 - - "≇": [t: "⠌⠈⠱⠨⠅"] # 0x2247 - - "≈": [t: "⠈⠱⠈⠱"] # 0x2248 - - "≉": [t: "⠌⠈⠱⠈⠱"] # 0x2249 - - "≊": [t: "⠈⠱⠈⠱⠱"] # 0x224a - - "≍": [t: "⠈⠣⠠⠣"] # 0x224d - - "≎": [t: "⠈⠣⠠⠣"] # 0x224e - - "≐": [t: "⠐⠨⠅⠣⠡⠻"] # 0x2250 - - "≑": [t: "⠐⠨⠅⠩⠡⠣⠡⠻"] # 0x2251 - - "≗": [t: "⠐⠨⠅⠣⠨⠡⠻"] # 0x2257 - - "≘": [t: "⠐⠨⠅⠣⠫⠁⠻"] # 0x2258 - - "≙": [t: "⠐⠨⠅⠣⠸⠣⠻"] # 0x2259 - - "≚": [t: "⠐⠨⠅⠣⠸⠩⠻"] # 0x225a - - "≛": [t: "⠐⠨⠅⠣⠫⠸⠎⠻"] # 0x225b - - "≜": [t: "⠐⠨⠅⠣⠫⠞⠻"] # 0x225c - - "≝": [t: "⠐⠨⠅⠣⠙⠑⠋⠻"] # 0x225d - - "≞": [t: "⠐⠨⠅⠣⠍⠻"] # 0x225e - - "≟": [t: "⠐⠨⠅⠣⠸⠦⠻"] # 0x225f - - "≠": [t: "⠌⠨⠅"] # 0x2260 - - "≢": [t: "⠌⠸⠇"] # 0x2262 - - "≤": [t: "⠐⠅⠱"] # 0x2264 - - "≥": [t: "⠨⠂⠱"] # 0x2265 - - "≦": [t: "⠐⠅⠨⠅"] # 0x2266 - - "≧": [t: "⠨⠂⠨⠅"] # 0x2267 - - "≨": [t: "⠐⠅⠌⠨⠅"] # 0x2268 - - "≩": [t: "⠨⠂⠌⠨⠅"] # 0x2269 - - "≪": [t: "⠐⠅⠈⠐⠅⠻"] # 0x226a - - "≫": [t: "⠨⠂⠈⠨⠂⠻"] # 0x226b - - "≭": [t: "⠌⠈⠣⠠⠣"] # 0x226d - - "≮": [t: "⠌⠐⠅"] # 0x226e - - "≯": [t: "⠌⠨⠂"] # 0x226f - - "≰": [t: "⠌⠐⠅⠱"] # 0x2270 - - "≱": [t: "⠌⠨⠂⠱"] # 0x2271 - - "≲": [t: "⠐⠅⠈⠱"] # 0x2272 - - "≳": [t: "⠨⠂⠈⠱"] # 0x2273 - - "≴": [t: "⠌⠐⠅⠈⠱"] # 0x2274 - - "≵": [t: "⠌⠨⠂⠈⠱"] # 0x2275 - - "≶": [t: "⠐⠅⠨⠂"] # 0x2276 - - "≷": [t: "⠨⠂⠐⠅"] # 0x2277 - - "≸": [t: "⠌⠐⠅⠨⠂"] # 0x2278 - - "≹": [t: "⠌⠨⠂⠐⠅"] # 0x2279 - - "≺": [t: "⠨⠐⠅"] # 0x227a - - "≻": [t: "⠨⠨⠂"] # 0x227b - - "≼": [t: "⠨⠐⠅⠱"] # 0x227c - - "≽": [t: "⠨⠨⠐⠱"] # 0x227d - - "≾": [t: "⠨⠐⠅⠈⠱"] # 0x227e - - "≿": [t: "⠨⠨⠂⠈⠱"] # 0x227f - - "⊀": [t: "⠌⠨⠐⠅"] # 0x2280 - - "⊁": [t: "⠌⠨⠐⠅"] # 0x2281 - - "⊂": [t: "⠸⠐⠅"] # 0x2282 - - "⊃": [t: "⠸⠨⠂"] # 0x2283 - - "⊄": [t: "⠌⠸⠐⠅"] # 0x2284 - - "⊅": [t: "⠌⠸⠨⠂"] # 0x2285 - - "⊆": [t: "⠸⠐⠅⠱"] # 0x2286 - - "⊇": [t: "⠸⠨⠂⠱"] # 0x2287 - - "⊈": [t: "⠌⠸⠐⠅⠱"] # 0x2288 - - "⊉": [t: "⠌⠸⠨⠂⠱"] # 0x2289 - - "⊊": [t: "⠸⠐⠅⠌⠨⠅"] # 0x228a - - "⊋": [t: "⠸⠨⠂⠌⠨⠅"] # 0x228b - - "⊥": [t: "⠫⠏"] # 0x22a5 - - "⋀": [t: "⠈⠩"] # 0x22c0 - - "⋁": [t: "⠈⠬"] # 0x22c1 - - "⋂": [t: "⠨⠩"] # 0x22c2 - - "⋃": [t: "⠨⠬"] # 0x22c3 - - "⋄": [t: "⠫⠩"] # 0x22c4 - - "⋜": [t: "⠱⠐⠅"] # 0x22dc - - "⋝": [t: "⠱⠨⠂"] # 0x22dd - - "⟂": [t: "⠫⠏"] # 0x27c2 - - "⦀": [t: "⠳⠳⠳"] # 0x2980 - - "⦶": [t: "⠫⠉⠸⠫⠳⠻"] # 0x29b6 - - "⦷": [t: "⠫⠉⠸⠫⠫⠇⠻"] # 0x29b7 - - "⦸": [t: "⠫⠉⠸⠫⠸⠡⠻"] # 0x29b8 - - "⦹": [t: "⠫⠉⠸⠫⠫⠏⠻"] # 0x29b9 - - "⦿": [t: "⠫⠉⠸⠫⠔⠔⠻"] # 0x29bf - - "⧀": [t: "⠫⠉⠸⠫⠐⠅⠻"] # 0x29c0 - - "⧁": [t: "⠫⠉⠸⠫⠨⠂⠻"] # 0x29c1 - - "⨀": [t: "⠫⠉⠸⠫⠡⠻"] # 0x2a00 - - "⨁": [t: "⠫⠉⠸⠫⠬⠻"] # 0x2a01 - - "⨂": [t: "⠫⠉⠸⠫⠈⠡⠻"] # 0x2a02 - - "⪯": [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: "⠈⠎"] # 0xfe69 - - "$": [t: "⠈⠎"] # 0xff04 - - "_": [t: "⠱"] # 0xff3f - - "~": [t: "⠈⠱"] # 0xff5e - - "¢": [t: "⠈⠉"] # 0xffe0 - - "£": [t: "⠈⠇"] # 0xffe1 - - " ̄": [t: "⠱"] # 0xffe3 - - "¥": [t: "⠈⠽"] # 0xffe5 - - # --- Nemeth Default Characters rest chars. --- - - - "ℵ": [t: "⠠⠠⠁"] # 0x2135 - - "ℶ": [t: "⠠⠠⠃"] # 0x2136 - - "ℷ": [t: "⠠⠠⠉"] # 0x2137 - - "ℸ": [t: "⠠⠠⠙"] # 0x2138 - - "à": [t: "⠈⠁"] # 0xe0 - - "á": [t: "⠈⠁"] # 0xe1 - - "â": [t: "⠈⠁"] # 0xe2 - - "ã": [t: "⠈⠁"] # 0xe3 - - "ä": [t: "⠈⠁"] # 0xe4 - - "å": [t: "⠈⠁"] # 0xe5 - - "ç": [t: "⠈⠉"] # 0xe7 - - "è": [t: "⠈⠑"] # 0xe8 - - "é": [t: "⠈⠑"] # 0xe9 - - "ê": [t: "⠈⠑"] # 0xea - - "ë": [t: "⠈⠑"] # 0xeb - - "ì": [t: "⠈⠊"] # 0xec - - "í": [t: "⠈⠊"] # 0xed - - "î": [t: "⠈⠊"] # 0xee - - "ï": [t: "⠈⠊"] # 0xef - - "ñ": [t: "⠈⠝"] # 0xf1 - - "ò": [t: "⠈⠕"] # 0xf2 - - "ó": [t: "⠈⠕"] # 0xf3 - - "ô": [t: "⠈⠕"] # 0xf4 - - "õ": [t: "⠈⠕"] # 0xf5 - - "ö": [t: "⠈⠕"] # 0xf6 - - "ù": [t: "⠈⠥"] # 0xf9 - - "ú": [t: "⠈⠥"] # 0xfa - - "û": [t: "⠈⠥"] # 0xfb - - "ü": [t: "⠈⠥"] # 0xfc - - "ý": [t: "⠈⠽"] # 0xfd - - "ÿ": [t: "⠈⠽"] # 0xff - - - "À": [t: "⠠⠈⠁"] # 0xc0 - - "Á": [t: "⠠⠈⠁"] # 0xc1 - - "Â": [t: "⠠⠈⠁"] # 0xc2 - - "Ã": [t: "⠠⠈⠁"] # 0xc3 - - "Ä": [t: "⠠⠈⠁"] # 0xc4 - - "Å": [t: "⠠⠈⠁"] # 0xc5 - - "Ç": [t: "⠠⠈⠉"] # 0xc7 - - "È": [t: "⠠⠈⠑"] # 0xc8 - - "É": [t: "⠠⠈⠑"] # 0xc9 - - "Ê": [t: "⠠⠈⠑"] # 0xca - - "Ë": [t: "⠠⠈⠑"] # 0xcb - - "Ì": [t: "⠠⠈⠊"] # 0xcc - - "Í": [t: "⠠⠈⠊"] # 0xcd - - "Î": [t: "⠠⠈⠊"] # 0xce - - "Ï": [t: "⠠⠈⠊"] # 0xcf - - "Ñ": [t: "⠠⠈⠝"] # 0xd1 - - "Ò": [t: "⠠⠈⠕"] # 0xd2 - - "Ó": [t: "⠠⠈⠕"] # 0xd3 - - "Ô": [t: "⠠⠈⠕"] # 0xd4 - - "Ö": [t: "⠠⠈⠕"] # 0xd6 - - "Ù": [t: "⠠⠈⠥"] # 0xd9 - - "Ú": [t: "⠠⠈⠥"] # 0xda - - "Û": [t: "⠠⠈⠥"] # 0xdb - - "Ü": [t: "⠠⠈⠥"] # 0xdc - - "Ý": [t: "⠠⠈⠽"] # 0xdd - - "←": [t: "⠫⠪⠒⠒"] # 0x2190 - - - "↑": # 0x2191 - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠣"] - else: [t: "⠫⠣⠒⠒⠕"] - - - "↓": # 0x2193 - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠩"] - else: [t: "⠫⠩⠒⠒⠕"] - - - "↔": [t: "⠫⠪⠒⠒⠕"] # 0x2194 - - "↕": [t: "⠫⠣⠩⠪⠒⠒⠕"] # 0x2195 - - "↖": [t: "⠫⠘⠪⠒⠒"] # 0x2196 - - "↗": [t: "⠫⠘⠒⠒⠕"] # 0x2197 - - "↘": [t: "⠫⠰⠒⠒⠕"] # 0x2198 - - "↙": [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: "⠫⠠⠳⠒⠒⠕"] # 0x21b4 - - "↵": [t: "⠫⠩⠠⠳⠒⠒⠕"] # 0x21b5 - - "↶": [t: "⠫⠢⠔⠕"] # 0x21b6 - - "↷": [t: "⠫⠪⠢⠔"] # 0x21b7 - - "↺": [t: "⠫⠢⠔⠕"] # 0x21ba - - "↻": [t: "⠫⠪⠢⠔"] # 0x21bb - - "⇄": [t: "⠫⠒⠒⠕⠫⠪⠒⠒"] # 0x21c4 - - "⇅": [t: "⠫⠣⠒⠒⠕⠐⠫⠩⠒⠒⠕"] # 0x21c5 - - "⇆": [t: "⠫⠪⠒⠒⠫⠒⠒⠕"] # 0x21c6 - - "⇇": [t: "⠫⠚⠒⠒⠫⠚⠒⠒"] # 0x21c7 - - "⇈": [t: "⠫⠣⠒⠒⠕⠐⠫⠣⠒⠒⠕"] # 0x21c8 - - "⇉": [t: "⠫⠒⠒⠕⠫⠒⠒⠕"] # 0x21c9 - - "⇊": [t: "⠫⠩⠒⠒⠕⠐⠫⠩⠒⠒⠕"] # 0x21ca - - "⇍": [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 FIX: how to represent triple shafts? - - "⇛": [t: "⠫⠶⠶⠕⠕"] # 0x21db FIX: how to represent triple shafts? - - "⇜": [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: "⠫⠣⠸⠪⠒⠒⠕"] # 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: "⠙"] # 0x2146 - - "ⅇ": [t: "⠑"] # 0x2147 - - "ⅈ": [t: "⠊"] # 0x2148 - - "⌜": [t: "⠈⠘⠷"] # 0x231c - - "⌝": [t: "⠈⠘⠾"] # 0x231d - - "⌞": [t: "⠈⠰⠷"] # 0x231e - - "⌟": [t: "⠈⠰⠾"] # 0x231f - - "〈": [t: "⠨⠨⠷"] # 0x2329 - - "〉": [t: "⠨⠨⠾"] # 0x232a - - "⏞": [t: "⠨⠷"] # 0x23de - - "⏟": [t: "⠨⠾"] # 0x23df - - "⎴": [t: "⠈⠷"] # 0x23b4 - - "⎵": [t: "⠈⠾"] # 0x23b5 - - "⟪": [t: "⠨⠨⠨⠷"] # 0x27ea - - "⟫": [t: "⠨⠨⠨⠾"] # 0x27eb - - "〈": [t: "⠨⠨⠷"] # 0x3008 - - "〉": [t: "⠨⠨⠾"] # 0x3009 - - "《": [t: "⠨⠨⠨⠷"] # 0x300a - - "》": [t: "⠨⠨⠨⠾"] # 0x300b - - "「": [t: "⠈⠘⠷"] # 0x300c - - "」": [t: "⠈⠘⠾"] # 0x300d - - "↼": [t: "⠫⠈⠪⠒⠒"] # 0x21bc - - "↽": [t: "⠫⠠⠪⠒⠒"] # 0x21bd - - "↾": [t: "⠫⠣⠒⠒⠠⠕"] # 0x21be - - "↿": [t: "⠫⠣⠒⠒⠈⠕"] # 0x21bf - - "⇀": [t: "⠫⠒⠒⠈⠕"] # 0x21c0 - - "⇁": [t: "⠫⠒⠒⠠⠕"] # 0x21c1 - - "⇂": [t: "⠫⠪⠒⠒⠈⠕"] # 0x21c2 - - "⇃": [t: "⠫⠩⠒⠒⠠⠕"] # 0x21c3 - - "⇋": # 0x21cb - - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠪⠒⠫⠒⠕"] - else: [t: "⠫⠈⠪⠒⠒⠫⠒⠒⠈⠕"] - - "⇌": # 0x21cc - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] - - - "℔": [t: "⠳"] # 0x2114 - - "™": [t: "⠘⠞"] # 0x2122 - - "Å": [t: "⠈⠠⠁"] # 0x212b - - "­": [t: "⠤"] # 0xad - - " ": [t: "W"] # 0x2000 - - " ": [t: "W"] # 0x2001 - - " ": [t: "W"] # 0x2002 - - " ": [t: "W"] # 0x2003 - - " ": [t: "W"] # 0x2004 - - " ": [t: "W"] # 0x2005 - - " ": [t: "W"] # 0x2006 - - " ": [t: "W"] # 0x2007 - - " ": [t: "W"] # 0x2008 - - " ": [t: "W"] # 0x2009 - - " ": [t: "W"] # 0x200a - - "​": [t: "W"] # 0x200b - - "
": [t: "W"] # 0x2028 - - "
": [t: "W"] # 0x2029 - - " ": [t: "W"] # 0x202f - - " ": [t: "W"] # 0x205f - - "₣": [t: "⠈⠋"] # 0x20A3 (French franc sign - - "₦": [t: "⠈⠝"] # 0x2046 (Naira sign) - - "₩": [t: "⠈⠺"] # 0x20A9 (Won sign) - - "€": [t: "⠈⠑"] # 0x20AC (Euro sign) - - "": [t: "W"] # 0xfeff - - "☆": [t: "⠫⠎"] # 0x2606 - - "✕": [t: "⠈⠡"] # 0x2715 - - "¼": [t: "⠹⠂⠌⠲⠼"] # 0xbc - - "½": [t: "⠹⠂⠌⠆⠼"] # 0xbd - - "¾": [t: "⠹⠒⠌⠲⠼"] # 0xbe - - "⅐": [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: "⠹⠴⠌⠒⠼"] # 0x2189 - - "㉈": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠂⠴⠻⠻"] # 0x3248 - - "㉉": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠆⠴⠻⠻"] # 0x3249 - - "㉊": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠒⠴⠻⠻"] # 0x324a - - "㉋": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠲⠴⠻⠻"] # 0x324b - - "㉌": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠢⠴⠻⠻"] # 0x324c - - "㉍": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠖⠴⠻⠻"] # 0x324d - - "㉎": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠶⠴⠻⠻"] # 0x324e - - "㉏": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠦⠴⠻⠻"] # 0x324f - - - - "‘": [t: "P⠠⠦"] # 0x2018 (Left single quotation mark) - - "’": [t: "P⠠⠴"] # 0x2019 (Right single quotation mark) - - "“": [t: "P⠦"] # 0x201C (Left double quotation mark) - - "”": [t: "P⠴"] # 0x201D (Right double quotation mark) - - "‰": [t: "`00"] - - "ℏ": [t: "⠈⠓"] # 0x210F (Planck constant over two pi) - - "ℓ": [t: "TE⠇"] # 0x2113 (Script small l (differs from 1d4c1: 4-56-123)) - - "℘": [tc: "TCL⠏"] # 0x2118 (Script Capital P) -# - "ℝ": [t: ""] # 0x211D (Double-struck capital R (no nemeth double struck)) -# - "ⅆ": [t: ""] # 0x2146 (Double-struck italic small d) -# - "ⅇ": [t: ""] # 0x2147 (Double-struck italic small e) -# - "⇚": [t: "⠀⠫⠪⠸⠸⠀"] # 0x21DA (Leftwards triple arrow) -# - "⇛": [t: "⠀⠫⠸⠸⠕⠀"] # 0x21DB (Rightwards triple arrow) - - "∔": [t: "⠐⠬⠣⠡⠻"] # 0x2214 (Dot plus) - - "√": [t: "⠜"] # 0x221A (Square root) - - "∛": [t: "⠣⠒⠜"] # 0x221B (Cube root) - - "∜": [t: "⠣⠲⠜"] # 0x221C (Fourth root) - - "∡": [t: "⠫⠪⠸⠫⠫⠁⠻"] # 0x2221 (Measured angle) - - "∹": [t: "⠤⠐⠂"] # 0x2239 (Excess) - - "∺": [t: "⠐⠤⠩⠡⠡⠣⠡⠡⠻"] # 0x223A (Geometric proportion) - - "∻": [t: "⠐⠈⠱⠩⠡⠣⠡⠻"] # 0x223B (Homothetic) - - "∽": [t: "⠠⠱"] # 0x223D (Reversed tilde) - - "≋": [t: "⠈⠱⠈⠱⠈⠱"] # 0x224B (Triple tilde) - - "≌": [t: "⠠⠱⠨⠅"] # 0x224C (All equal to) - - "≏": [t: "⠈⠣⠱"] # 0x224F (Difference between) - - "≔": [t: "⠐⠂⠨⠅"] # 0x2254 (Colon equals) - - "≕": [t: "⠨⠅⠐⠂"] # 0x2255 (Equals colon) - - "≖": [t: "⠀⠨⠡⠈⠨⠅⠻⠀"] # 0x2256 (Ring in equal to) - - "⊌": [t: "⠨⠬⠈⠫⠪⠒⠻"] # 0x228C (Multiset) - - "⊍": [t: "⠡⠈⠨⠬⠻"] # 0x228D (Multiset multiplication) - - "⊎": [t: "⠬⠈⠨⠬⠻"] # 0x228E (Multiset union) - - "⊕": [t: "⠫⠉⠸⠫⠬⠻"] # 0x2295 (Circled plus) - - "⊖": [t: "⠫⠉⠸⠫⠤⠻"] # 0x2296 (Circled minus) - - "⊗": [t: "⠫⠉⠸⠫⠈⠡⠻"] # 0x2297 (Circled times) - - "⊘": [t: "⠫⠉⠸⠫⠸⠌⠻"] # 0x2298 (Circled division slash) - - "⊙": [t: "⠫⠉⠸⠫⠡⠻"] # 0x2299 (Circled dot operator) - - "⊚": [t: "⠫⠉⠸⠫⠘⠨⠡⠻"] # 0x229a (Circled ring operator) - - "⊛": [t: "⠫⠉⠸⠫⠈⠼⠻"] # 0x229B (Circled asterisk operator) - - "⊜": [t: "⠫⠉⠸⠫⠨⠅⠻"] # 0x229C (Circled equals) - - "⊝": [t: "⠫⠉⠸⠫⠤⠤⠻"] # 0x229D (Circled dash) - - "⊞": [t: "⠫⠲⠸⠫⠬⠻"] # 0x229E (Squared plus) - - "⊟": [t: "⠫⠲⠸⠫⠤⠻"] # 0x229F (Squared minus) - - "⊠": [t: "⠫⠲⠸⠫⠈⠡⠻"] # 0x22A0 (Squared times) - - "⊡": [t: "⠫⠲⠸⠫⠡⠻"] # 0x22A1 (Squared dot operator) - - "⊢": [t: "⠫⠳⠒"] # 0x22A2 (Right tack) - - "⊣": [t: "⠫⠒⠳"] # 0x22A3 (Left tack) - - "⊤": [t: "⠫⠩⠳⠒"] # 0x22A4 (Down tack) -# - "⊥": [t: "⠀⠫⠏⠀ or ⠀⠫⠣⠳⠒⠀"] # 0x22A5 (Up tack (See also 27C2)) -# - "⊦": [t: ""] # 0x22A6 (Assertion) -# - "⊨": [t: ""] # 0x22A8 (TRUE) -# - "⊲": [t: ""] # 0x22B2 (Normal subgroup of) -# - "⊳": [t: ""] # 0x22B3 (Contains as normal subgroup) -# - "⊴": [t: ""] # 0x22B4 (Normal subgroup of or equal to) -# - "⊵": [t: ""] # 0x22B5 (Contains as normal subgroup or equal to) - - "⊶": [t: "⠫⠨⠡⠒⠡"] # 0x22B6 (Original of) - - "⊷": [t: "⠫⠡⠒⠨⠡"] # 0x22B7 (Image of) - - "⊸": [t: "⠫⠒⠨⠡"] # 0x22B8 (Multimap) -# - "⊾": [t: ""] # 0x22BE (Right angle w arc) - - "⊿": [t: "⠫⠞⠨⠗⠻"] # 0x22BF (Right triangle) - - "⋅": [t: "⠡"] # 0x22C5 (Dot operator) - - "∙": [t: "⠡"] # 0x2219 -- similar looking to 0x22c5, so using the same braille - - "⋆": [t: "⠫⠎"] # 0x22C6 (Star operator) - - "⋍": [t: "⠠⠱⠱"] # 0x22CD (Reversed tilde equals) - - "⋖": [t: "⠡⠈⠐⠅⠻"] # 0x22D6 (Less-than w dot) - - "⋗": [t: "⠡⠈⠨⠂⠻"] # 0x22D7 (Greater-than w dot) - - "⋘": [t: "⠐⠅⠈⠐⠅⠈⠐⠅⠻"] # 0x22D8 (Much less-than) - - "⋙": [t: "⠨⠂⠈⠨⠂⠈⠨⠂⠻"] # 0x22D9 (Much greater-than) - - "⋲": [t: "⠱⠈⠈⠑⠻"] # 0x22F2 (Element of w long horizontal stroke) - - "⋵": [t: "⠐⠈⠑⠣⠡⠻"] # 0x22F5 (Element of w dot above) - - "⋶": [t: "⠱⠈⠑"] # 0x22F6 (Element of w overbar) - - "⋺": [t: "⠱⠈⠈⠢⠻"] # 0x22FA (Contains w long horizontal stroke) - - "⋽": [t: "⠱⠈⠢"] # 0x22FD (Contains w overbar) - - "⌈": [t: "⠈⠘⠷"] # 0x2308 (Left ceiling) - - "⌉": [t: "⠈⠘⠾"] # 0x2309 (Right ceiling) - - "⌊": [t: "⠈⠰⠷"] # 0x230A (Left floor) - - "⌋": [t: "⠈⠰⠾"] # 0x230B (Right floor) - - "⌢": [t: "⠫⠁"] # 0x2322 (Frown) - - "⌣": [t: "⠫⠄"] # 0x2323 (Smile) - - "■": [t: "⠫⠸⠲"] # 0x25A0 (Filled square) - - "▫": [t: "⠫⠲"] # 0x25AB (Small white Square) - - "□": [t: "⠫⠲"] # 0x25A1 (Square) - - "◻": [t: "⠫⠲"] # 0x25FB (Square) - - "▬": [t: "⠫⠸⠗"] # 0x25AC (Filled rectangle) - - "▭": [t: "⠫⠗"] # 0x25AD (Rectangle) - - "▲": [t: "⠫⠸⠞"] # 0x25B2 (Black up-pointing triangle) - - "△": [t: "⠫⠞"] # 0x25B3 (White up-pointing triangle) - - "▼": [t: "⠸⠨⠫"] # 0x25BC (Black down-pointing triangle) - - "○": [t: "⠫⠉"] # 0x25CB (White circle) - - "●": [t: "⠫⠸⠉"] # 0x25CF (Black circle) - - "◫": [t: "⠫⠲⠸⠫⠳⠻"] # 0x25EB (White square w vertical bisecting line) -# - "⊥": [t: "⠫⠏"] # 0x27C2 (Perpendicular) - - "⟃": [t: "⠨⠡⠈⠸⠐⠅⠻"] # 0x27C3 (Open subset) - - "⟄": [t: "⠨⠡⠈⠸⠨⠂⠻"] # 0x27C4 (Open superset) - - "⟜": [t: "⠫⠨⠡⠒⠒"] # 0x27DC (Left multimap) - - "⟝": [t: "⠫⠳⠒⠒"] # 0x27DD (Long right tack) - - "⟞": [t: "⠫⠒⠒⠳"] # 0x27DE (Long left tack) - - "⟟": [t: "⠫⠣⠳⠒⠒⠨⠡"] # 0x27DF (Up tack w circle above) - - "⟦": [t: "⠈⠸⠷"] # 0x27E6 (Mathematical left white square bracket) - - "⟧": [t: "⠈⠸⠾"] # 0x27E7 (Mathematical right white square bracket) - - "⟨": [t: "⠨⠨⠷"] # 0x27E8 (Mathematical left angle bracket) - - "⟩": [t: "⠨⠨⠾"] # 0x27E9 (Mathematical right angle bracket) - - "⟵": [t: "⠀⠫⠪⠒⠒⠒⠀"] # 0x27F5 (Long leftwards arrow ) - - "⟶": [t: "⠀⠫⠒⠒⠒⠕⠀"] # 0x27F6 (Long rightwards arrow) - - "⟷": [t: "⠀⠫⠪⠒⠒⠒⠕⠀"] # 0x27F7 (Long left right arrow) - - "⟸": [t: "⠀⠫⠪⠶⠶⠶⠀"] # 0x27F8 (Long leftwards arrow ) - - "⟹": [t: "⠀⠫⠶⠶⠶⠕⠀"] # 0x27F9 (Long rightwards arrow) - - "⟺": [t: "⠀⠫⠪⠶⠶⠶⠕⠀"] # 0x27FA (Long left right arrow) - - "⟻": [t: "⠀⠫⠪⠒⠒⠒⠳⠀"] # 0x27FB (Long leftwards arrow from bar) - - "⟼": [t: "⠀⠫⠳⠒⠒⠒⠕⠀"] # 0x27FC (Long rightwards arrow from bar) - - "⟽": [t: "⠀⠫⠪⠶⠶⠶⠳⠀"] # 0x27FD (Long leftwards double arrow from bar) - - "⟾": [t: "⠀⠫⠳⠶⠶⠶⠕⠀"] # 0x27FE (Long rightwards double arrow from bar) - - "⟿": [t: "⠀⠫⠢⠤⠔⠒⠢⠤⠔⠒⠢⠕⠀"] # 0x27FF (Long rightwards squiggle arrow) - - "⤡": [t: "⠀⠫⠘⠪⠒⠒⠕⠀"] # 0x2921 (North west and south east arrow) - - "⤢": [t: "⠀⠫⠰⠪⠒⠒⠕⠀"] # 0x2922 (North east and south west arrow) - - "🣐": # 0x1f8d0 (Long rightwards arrow over leftwards arrow (equilibrium)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠠⠪⠒⠒"] - - "🣑": # 0x1f8d1 (Long rightwards harpoon over leftwards harpoon (equilibrium)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠠⠪⠒⠒"] - - "🣒": # 0x1f8d2 (Long rightwards harpoon over short leftwards harpoon (equilibrium)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] - - "🣓": # 0x1f8d3 (Short rightwards harpoon over long leftwards harpoon (equilibrium, trend to the right)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒⠒"] - else: [t: "⠫⠒⠈⠕⠫⠠⠪⠒⠒"] - - "🣔": # 0x1f8d4 (Long leftwards harpoon over short rightwards harpoon (equilibrium, trend to the left)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠪⠒⠒⠫⠒⠕"] - else: [t: "⠫⠈⠪⠒⠒⠫⠒⠠⠕"] - - "🣕": # 0x1f8d5 (Short leftwards harpoon over long rightwards harpoon (equilibrium, trend to the left)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠪⠒⠫⠒⠒⠕"] - else: [t: "⠫⠈⠪⠒⠫⠒⠒⠠⠕"] - - "⥂": # 0x2942 (Long rightwards harpoon over short leftwards harpoon (equilibrium)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] - - "⥄": # 0x2944 (Short rightwards harpoon over long leftwards harpoon (equilibrium, trend to the right)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒⠒"] - else: [t: "⠫⠒⠈⠕⠫⠠⠪⠒⠒"] - - "⥃": # 0x2943 (Long leftwards harpoon over short rightwards harpoon (equilibrium, trend to the left)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠪⠒⠒⠫⠒⠕"] - else: [t: "⠫⠈⠪⠒⠒⠫⠒⠠⠕"] - - "🣖": [t: "⠌⠫⠒⠒⠕"] # 0x1f8d6 (long rightwards arrow with through X) - - "🣗": [t: "⠌⠫⠒⠒⠕"] # 0x1f8d7 (long rightwards arrow with double slash) chem code says it is same as 0x1f8d6 - - - - "⦃": [t: "⠨⠸⠷"] # 0x2983 (Left white curly bracket) - - "⦄": [t: "⠨⠸⠾"] # 0x2984 (Right white curly bracket) - - "⦑": [t: "⠡⠈⠨⠨⠷⠻"] # 0x2991 (Left angle bracket w dot) - - "⦒": [t: "⠡⠈⠨⠨⠾⠻"] # 0x2992 (Right angle bracket w dot) - - "⧄": [t: "⠫⠲⠸⠫⠔⠻"] # 0x29C4 (Square rising diagonal slash) - - "⧅": [t: "⠫⠲⠸⠫⠢⠻"] # 0x29C5 (Square falling diagonal slash) - - "⧆": [t: "⠫⠲⠸⠫⠈⠼⠻"] # 0x29C6 (Squared asterisk) - - "⧊": [t: "⠐⠫⠞⠣⠡⠻"] # 0x29CA (Triangle w dot above) - - "⧋": [t: "⠫⠞⠱"] # 0x29CB (Triangle w underbar) - - "⧌": [t: "⠫⠞⠎"] # 0x29CC (s in triangle §114) - - "⧦": [t: "⠫⠳⠶⠶⠳"] # 0x29E6 (Gleich stark) - - "⨌": [t: "⠮⠮⠮⠮"] # 0x2A0C (Quadruple integral operator) - - "⨍": [t: "⠮⠈⠱⠻"] # 0x2A0D (Finite part integral) - - "⨎": [t: "⠮⠈⠱⠱⠻"] # 0x2A0E (Integral w double stroke) - - "⨖": [t: "⠮⠈⠫⠲⠻"] # 0x2A16 (Quaternion integral operator) - - "⨘": [t: "⠮⠈⠈⠡⠻"] # 0x2A18 (Integral w times sign) - - "⨙": [t: "⠮⠈⠨⠩⠻"] # 0x2A19 (Integral w intersection) - - "⨚": [t: "⠮⠈⠨⠬⠻"] # 0x2A1A (Integral w union) - - "⨛": [t: "⠣⠮"] # 0x2A1B (Integral w overbar (upper)) - - "⨜": [t: "⠩⠮"] # 0x2A1C (Integral w underbar (lower)) - - "⨢": [t: "⠐⠬⠣⠨⠡⠻"] # 0x2A22 (Plus sign w small circle above) - - "⨣": [t: "⠐⠬⠣⠸⠣⠻"] # 0x2A23 (Plus sign w circumflex accent above) - - "⨤": [t: "⠐⠬⠣⠈⠱⠻"] # 0x2A24 (Plus sign w tilde above) - - "⨥": [t: "⠐⠬⠩⠡⠻"] # 0x2A25 (Plus sign w dot below) - - "⨪": [t: "⠐⠱⠩⠡⠻"] # 0x2A2A (Minus sign w dot below) - - "⨰": [t: "⠐⠈⠡⠣⠡⠻"] # 0x2A30 (Multiplication sign w dot above) - - "⨱": [t: "⠈⠡⠱"] # 0x2A31 (Multiplication sign w underbar) - - "⨸": [t: "⠫⠉⠸⠫⠨⠌⠻"] # 0x2A38 (Circled division sign) - - "⨹": [t: "⠫⠞⠸⠫⠬⠻"] # 0x2A39 (Plus sign in triangle) - - "⨺": [t: "⠫⠞⠸⠫⠤⠻"] # 0x2A3A (Minus sign in triangle) - - "⨻": [t: "⠫⠞⠸⠫⠈⠡⠻"] # 0x2A3B (Multiplication sign in triangle) - - "⩀": [t: "⠡⠈⠨⠩⠻"] # 0x2A40 (Intersection w dot) - - "⩁": [t: "⠤⠈⠨⠬⠻"] # 0x2A41 (Union w minus sign) - - "⩂": [t: "⠱⠨⠬"] # 0x2A42 (Union w overbar) - - "⩃": [t: "⠱⠨⠩"] # 0x2A43 (Intersection w overbar) - - "⩑": [t: "⠐⠈⠩⠣⠡⠻"] # 0x2A51 (Logical AND w dot above) - - "⩒": [t: "⠐⠈⠬⠣⠡⠻"] # 0x2A52 (Logical OR w dot above) - - "⩜": [t: "⠱⠈⠈⠩⠻"] # 0x2A5C (Logical AND with horizontal dash) - - "⩝": [t: "⠱⠈⠈⠬⠻"] # 0x2A5D (Logical OR with horizontal dash) - - "⩞": [t: "⠱⠱⠈⠩"] # 0x2A5E (Logical AND w double overbar) - - "⩟": [t: "⠈⠩⠱"] # 0x2A5F (Logical AND w underbar) - - "⩠": [t: "⠈⠩⠱⠱"] # 0x2A60 (Logical AND w double underbar) - - "⩢": [t: "⠱⠱⠈⠬"] # 0x2A62 (Logical OR w double overbar) - - "⩣": [t: "⠈⠬⠱⠱"] # 0x2A63 (Logical OR w double underbar) - - "⩦": [t: "⠐⠨⠅⠩⠡⠻"] # 0x2A66 (Equals sign w dot below) - - "⩧": [t: "⠐⠸⠇⠣⠡⠻"] # 0x2A67 (Identical w dot above) - - "⩪": [t: "⠐⠈⠱⠣⠡⠻"] # 0x2A6A (Tilde operator w dot above) - - "⩬": [t: "⠈⠱⠱⠈⠱"] # 0x2A6C (Similar minus similar) - - "⩭": [t: "⠐⠈⠱⠨⠅⠣⠡⠻"] # 0x2A6D (Congruent w dot above) - - "⩮": [t: "⠐⠨⠅⠣⠈⠼⠻"] # 0x2A6E (Equals w asterisk) - - "⩯": [t: "⠐⠈⠱⠈⠱⠣⠸⠣⠻"] # 0x2A6F (Almost equal to w circumflex) - - "⩰": [t: "⠈⠱⠈⠱⠨⠅"] # 0x2A70 (Approximately equal or equal to) - - "⩱": [t: "⠨⠅⠬"] # 0x2A71 (Equals sign above plus sign) - - "⩲": [t: "⠬⠨⠅"] # 0x2A72 (Plus sign above equals sign) - - "⩳": [t: "⠨⠅⠈⠱"] # 0x2A73 (Equals sign above tilde operator) - - "⩴": [t: "⠐⠂⠐⠂⠨⠅"] # 0x2A74 (Double colon equal) - - "⩵": [t: "⠨⠅⠨⠅"] # 0x2A75 (Two consecutive equals signs) - - "⩶": [t: "⠨⠅⠨⠅⠨⠅"] # 0x2A76 (Three consecutive equals signs) - - "⩷": [t: "⠐⠨⠅⠩⠡⠡⠣⠡⠡⠻"] # 0x2A77 (Equals sign w two dots above and two dots below) - - "⩸": [t: "⠐⠸⠇⠣⠡⠡⠡⠡⠻"] # 0x2A78 (Equivalent w four dots above) - - "⩹": [t: "⠐⠅⠈⠨⠡⠻"] # 0x2A79 (Less than w circle inside) - - "⩺": [t: "⠨⠂⠈⠨⠡⠻"] # 0x2A7A (Greater than w circle inside) - - "⩻": [t: "⠐⠐⠅⠣⠸⠦⠻"] # 0x2A7B (Less-than w question mark above) - - "⩼": [t: "⠐⠨⠂⠣⠸⠦⠻"] # 0x2A7C (Greater-than w question mark above) - - "⪅": [t: "⠐⠅⠈⠱⠈⠱"] # 0x2A85 (Less-than or approximate) - - "⪆": [t: "⠨⠂⠈⠱⠈⠱"] # 0x2A86 (Greater-than or approximate) - - "⪋": [t: "⠐⠅⠨⠅⠨⠂"] # 0x2A8B (Less-than above double-line equal above greater-than) - - "⪌": [t: "⠨⠂⠨⠅⠐⠅"] # 0x2A8C (Greater-than above double-line equal above less-than) - - "⪍": [t: "⠐⠅⠈⠱⠱"] # 0x2A8D (Less-than above similar or equal to) - - "⪎": [t: "⠨⠂⠈⠱⠱"] # 0x2A8E (Greater-than above similar or equal to) - - "⪏": [t: "⠐⠅⠈⠱⠨⠂"] # 0x2A8F (Less-than above similar above greater-than) - - "⪐": [t: "⠨⠂⠈⠱⠐⠅"] # 0x2A90 (Greater-than above similar above less-than) - - "⪑": [t: "⠐⠅⠨⠂⠨⠅"] # 0x2A91 (Less-than above greater-than above double-line equal) - - "⪒": [t: "⠨⠂⠐⠅⠨⠅"] # 0x2A92 (Greater-than above less-than above double-line equal) - - "⪙": [t: "⠨⠅⠐⠅"] # 0x2A99 (Double-line equal to or less-than) - - "⪚": [t: "⠨⠅⠨⠂"] # 0x2A9A (Double-line equal to or greater-than) - - "⪝": [t: "⠈⠱⠐⠅"] # 0x2A9D (Similar or less-than) - - "⪞": [t: "⠈⠱⠨⠂"] # 0x2A9E (Similar or greater-than) - - "⪟": [t: "⠈⠱⠐⠅⠨⠅"] # 0x2A9F (Similar above less-than above equals sign) - - "⪠": [t: "⠈⠱⠨⠂⠨⠅"] # 0x2AA0 (Similar above greater-than above equals sign) - - "⪮": [t: "⠈⠣⠨⠅"] # 0x2AAE (Equals sign with bumpy above) - - "⪽": [t: "⠡⠈⠸⠐⠅⠻"] # 0x2ABD (Subset w dot) - - "⪾": [t: "⠡⠈⠸⠨⠂⠻"] # 0x2ABE (Superset w dot) - - "⪿": [t: "⠐⠸⠐⠅⠩⠬⠻"] # 0x2ABF (Subset with plus sign below) - - "⫀": [t: "⠐⠸⠨⠂⠩⠬⠻"] # 0x2AC0 (Superset with plus sign below) - - "⫁": [t: "⠐⠸⠐⠅⠩⠈⠡⠻"] # 0x2AC1 (Subset with multiplication sign below) - - "⫂": [t: "⠐⠸⠨⠂⠩⠈⠡⠻"] # 0x2AC2 (Superset with multiplication sign below) - - "⫅": [t: "⠸⠐⠅⠨⠅"] # 0x2AC5 (Subset of above equals sign) - - "⫆": [t: "⠸⠨⠂⠨⠅"] # 0x2AC6 (Superset of above equals sign) - - "⫇": [t: "⠸⠐⠅⠈⠱"] # 0x2AC7 (Subset of above tilde operator) - - "⫈": [t: "⠸⠨⠂⠈⠱"] # 0x2AC8 (Superset of above tilde operator) - - "⫉": [t: "⠸⠐⠅⠈⠱⠈⠱"] # 0x2AC9 (Subset of above almost equal to) - - "⫊": [t: "⠸⠨⠂⠈⠱⠈⠱"] # 0x2ACA (Superset of above almost equal to) - - "⫋": [t: "⠸⠐⠅⠌⠨⠅"] # 0x2ACB (Subset of above not equals sign) - - "⫌": [t: "⠸⠨⠂⠌⠨⠅"] # 0x2ACC (Superset of above not equals sign) - - "⫓": [t: "⠸⠐⠅⠸⠨⠂"] # 0x2AD3 (Subset above superset) - - "⫔": [t: "⠸⠨⠂⠸⠐⠅"] # 0x2AD4 (Superset above subset) - - "⫕": [t: "⠸⠐⠅⠸⠐⠅"] # 0x2AD5 (Subset above subset) - - "⫖": [t: "⠸⠨⠂⠸⠨⠂"] # 0x2AD6 (Superset above superset) - - "⫗": [t: "⠸⠨⠂⠐⠸⠐⠅"] # 0x2AD7 (Superset beside subset) - - "⫯": [t: "⠫⠩⠨⠡⠒⠒"] # 0x2AEF (Vertical line w circle above) - - "⫰": [t: "⠫⠣⠨⠡⠒⠒"] # 0x2AF0 (Vertical line w circle below) - - "⫱": [t: "⠫⠣⠨⠡⠒⠒⠳"] # 0x2AF1 (Down tack w circle below) - - "⫲": [t: "⠱⠈⠫⠇⠻"] # 0x2AF2 (Parallel w horizontal stroke) - - "⫳": [t: "⠈⠱⠈⠫⠇⠻"] # 0x2AF3 (Parallel w tilde operator) - - "⫴": [t: "⠳⠳⠳"] # 0x2AF4 (Triple vertical bar binary relation) - - "⬬": [t: "⠫⠸⠑"] # 0x2B2C (Black ellipse) - - "⬭": [t: "⠫⠑"] # 0x2B2D (White ellipse) - - "⬰": [t: "⠨⠡⠈⠫⠪⠒⠒⠻"] # 0x2B30 (Left arrow w small circle) - - "⬱": [t: "⠫⠪⠒⠒⠫⠪⠒⠒⠫⠪⠒⠒"] # 0x2B31 (Three leftwards arrows) - - "⬲": [t: "⠫⠪⠒⠒⠈⠫⠉⠸⠫⠬⠻"] # 0x2B32 (Left arrow w circled plus) - - "⮖": [t: "⠨⠡"] # 0x2B96 (Standard State Symbol) - - "⯃": [t: "⠫⠸⠦"] # 0x2BC3 (Horizontal black octagon) - - # SRE lists this as a custom transcription - - "⋊": [t: "⠈⠡⠳"] # 0x22ca (RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT) +--- +# To greatly simplify typeface/language generation, the chars have unique ASCII chars for them: +# Typeface: S: sans-serif, B: bold, T: script/blackboard, I: italic, R: Roman +# Language: E: English, D: German, G: Greek, V: Greek variants, H: Hebrew, U: Russian +# Indicators: C: capital, L: letter, N: number, P: punctuation, M: multipurpose +# Others: W -- whitespace that should be kept (e.g, in a numeral) +# SRE doesn't have H: Hebrew, so not encoded (yet) +# Some of the weird letters (e.g., circled letters) I didn't translate with the above because +# they don't interact with rules since they are enclosed in other chars. + + - "⨯": [t: "⠈⠡"] # U+2A2F(VECTOR OR CROSS PRODUCT) -- make the same as 0x00D7 (Multiplication sign) + + # --- Nemeth Default Alphabet bold chars. --- + - "𝐀-𝐙": # 0x1d400 - 0x1d419 + - tc: "BE" + - spell: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝐚-𝐳": # 0x1d41a - 0x1d433 + - tc: "BE" + - spell: "translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 + - tc: "BG" + - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝛂-𝛒": # 0x1d6c2 - 0x1d6d2 + - tc: "BG" + - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒', 'αβγδεζηθικλμνξοπρ')" + + - "𝛔-𝛖": # 0x1d6d4 - 0x1d6d6 + - tc: "BG" + - spell: "translate('.', '𝛔𝛕𝛖', 'στυ')" + + - "𝛘-𝛚": # 0x1d6d8 - 0x1d6da + - tc: "BG" + - spell: "translate('.', '𝛘𝛙𝛚', 'χψω')" + + - "𝛁": [tc: "BGL⠫"] # 0x1d6c1 + - "𝛓": [tc: "BVL⠎"] # 0x1d6d3 + - "𝛗": [tc: "BVL⠋"] # 0x1d6d7 + - "𝛛": [tc: "⠸⠈⠙"] # 0x1d6db + - "𝛜": [tc: "BGL⠑"] # 0x1d6dc + - "𝛝": [tc: "BVL⠹"] # 0x1d6dd + - "𝛞": [tc: "BGL⠅"] # 0x1d6de + - "𝛟": [tc: "BGL⠋"] # 0x1d6df + - "𝛠": [tc: "BGL⠗"] # 0x1d6e0 + - "𝛡": [tc: "BGL⠏"] # 0x1d6e1 + + - "𝟎-𝟗": # 0x1d7ce - 0x1d7d7 + - tc: "B" + - spell: "translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789')" + + # --- Nemeth Default Alphabet bold-fraktur chars. --- + + - "𝕬-𝖅": # 0x1d56c - 0x1d585 + - tc: "BD" + - spell: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝖆-𝖟": # 0x1d586 - 0x1d59f + - tc: "BD" + - spell: "translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz')" + + # --- Nemeth Default Alphabet bold-italic chars. --- + - "𝑨-𝒁": # 0x1d468 - 0x1d481 + - tc: "BE" + - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝒂-𝒛": # 0x1d482 - 0x1d49b + - tc: "BE" + - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝜜-𝜴": # 0x1d71c - 0x1d734 + - tc: "BG" + - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳𝜴', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝜵": [tc: "BGL⠫"] # 0x1d735 + + - "𝜶-𝝆": # 0x1d736 - 0x1d746 + - tc: "BG" + - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆', 'αβγδεζηθικλμνξοπρ')" + + - "𝝇": [tc: "BVL⠎"] # 0x1d747 + + - "𝝈-𝝊": # 0x1d748 - 0x1d74a + - tc: "BG" + - spell: "translate('.', '𝝈𝝉𝝊', 'στυ')" + + - "𝝋": [tc: "BVL⠋"] # 0x1d74b + + - "𝝌-𝝎": # 0x1d74c - 0x1d74e + - tc: "BG" + - spell: "translate('.', '𝝌𝝍𝝎', 'χψω')" + + - "𝝏": [tc: "BVL⠙"] # 0x1d74f + - "𝝐": [tc: "BGL⠑"] # 0x1d750 + - "𝝑": [tc: "BVL⠹"] # 0x1d751 + + - "𝝒-𝝕": # 0x1d752 - 0x1d755 + - tc: "BG" + - spell: "translate('.', '𝝒𝝓𝝔𝝕', 'κφρπ')" + + # --- Nemeth Default Alphabet bold-script chars. --- + + - "𝓐-𝓩": # 0x1d4d0 - 0x1d4e9 + - tc: "BTE" + - spell: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝓪-𝔃": # 0x1d4ea - 0x1d503 + - tc: "BTE" + - spell: "translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝚤-𝚥": # 0x1d6a4 - 0x1d6a5 + - tc: "" + - spell: "translate('.', '𝚤𝚥', '⠊⠚')" + +# Russian Alphabet (from Nemeth book) +# NOTE: is this translation needed? + - "А-Я": # 0x410 - 0x42f + - tc: "U" + - spell: "translate('.', 'АБВГДЕЗИЙКЛМНОПРСТУФЭ', 'ABVGDEZIYKLMNOPRSTUFE')" + + - "а-я": # 0x430 - 0x44f + - tc: "U" + - spell: "translate('.', 'абвгдезийклмнопрстуфэ', 'abvgdeziylkmnoprstufye')" + + - "①-⑨": # 0x2460 - 0x2473 + - tc: "⠫⠉⠸⠫⠼" + - spell: "translate('.', '①②③④⑤⑥⑦⑧⑨', '123456789')" + - tc: "⠻" + + - "⓪": [tc: "⠫⠉⠸⠫⠼⠴⠻"] # 0x24ea + + - "⑩": [tc: "⠫⠉⠸⠫⠼⠂⠴⠻"] # 0x2469 + - "⑪": [tc: "⠫⠉⠸⠫⠼⠂⠂⠻"] # 0x246a + - "⑫": [tc: "⠫⠉⠸⠫⠼⠂⠆⠻"] # 0x246b + - "⑬": [tc: "⠫⠉⠸⠫⠼⠂⠒⠻"] # 0x246c + - "⑭": [tc: "⠫⠉⠸⠫⠼⠂⠲⠻"] # 0x246d + - "⑮": [tc: "⠫⠉⠸⠫⠼⠂⠢⠻"] # 0x246e + - "⑯": [tc: "⠫⠉⠸⠫⠼⠂⠖⠻"] # 0x246f + - "⑰": [tc: "⠫⠉⠸⠫⠼⠂⠶⠻"] # 0x2470 + - "⑱": [tc: "⠫⠉⠸⠫⠼⠂⠦⠻"] # 0x2471 + - "⑲": [tc: "⠫⠉⠸⠫⠼⠂⠔⠻"] # 0x2472 + - "⑳": [tc: "⠫⠉⠸⠫⠼⠆⠴⠻"] # 0x2473 + + - "㉑": [tc: "⠫⠉⠸⠫⠼⠆⠂⠻"] # 0x3251 + - "㉒": [tc: "⠫⠉⠸⠫⠼⠆⠆⠻"] # 0x3252 + - "㉓": [tc: "⠫⠉⠸⠫⠼⠆⠒⠻"] # 0x3253 + - "㉔": [tc: "⠫⠉⠸⠫⠼⠆⠲⠻"] # 0x3254 + - "㉕": [tc: "⠫⠉⠸⠫⠼⠆⠢⠻"] # 0x3255 + - "㉖": [tc: "⠫⠉⠸⠫⠼⠆⠖⠻"] # 0x3256 + - "㉗": [tc: "⠫⠉⠸⠫⠼⠆⠶⠻"] # 0x3257 + - "㉘": [tc: "⠫⠉⠸⠫⠼⠆⠦⠻"] # 0x3258 + - "㉙": [tc: "⠫⠉⠸⠫⠼⠆⠔⠻"] # 0x3259 + - "㉚": [tc: "⠫⠉⠸⠫⠼⠒⠴⠻"] # 0x325a + - "㉛": [tc: "⠫⠉⠸⠫⠼⠒⠂⠻"] # 0x325b + - "㉜": [tc: "⠫⠉⠸⠫⠼⠒⠆⠻"] # 0x325c + - "㉝": [tc: "⠫⠉⠸⠫⠼⠒⠒⠻"] # 0x325d + - "㉞": [tc: "⠫⠉⠸⠫⠼⠒⠲⠻"] # 0x325e + - "㉟": [tc: "⠫⠉⠸⠫⠼⠒⠢⠻"] # 0x325f + - "㊱": [tc: "⠫⠉⠸⠫⠼⠒⠖⠻"] # 0x32b1 + - "㊲": [tc: "⠫⠉⠸⠫⠼⠒⠶⠻"] # 0x32b2 + - "㊳": [tc: "⠫⠉⠸⠫⠼⠒⠦⠻"] # 0x32b3 + - "㊴": [tc: "⠫⠉⠸⠫⠼⠒⠔⠻"] # 0x32b4 + - "㊵": [tc: "⠫⠉⠸⠫⠼⠲⠴⠻"] # 0x32b5 + - "㊶": [tc: "⠫⠉⠸⠫⠼⠲⠂⠻"] # 0x32b6 + - "㊷": [tc: "⠫⠉⠸⠫⠼⠲⠆⠻"] # 0x32b7 + - "㊸": [tc: "⠫⠉⠸⠫⠼⠲⠒⠻"] # 0x32b8 + - "㊹": [tc: "⠫⠉⠸⠫⠼⠲⠲⠻"] # 0x32b9 + - "㊺": [tc: "⠫⠉⠸⠫⠼⠲⠢⠻"] # 0x32ba + - "㊻": [tc: "⠫⠉⠸⠫⠼⠲⠖⠻"] # 0x32bb + - "㊼": [tc: "⠫⠉⠸⠫⠼⠲⠶⠻"] # 0x32bc + - "㊽": [tc: "⠫⠉⠸⠫⠼⠲⠦⠻"] # 0x32bd + - "㊾": [tc: "⠫⠉⠸⠫⠼⠲⠔⠻"] # 0x32be + - "㊿": [tc: "⠫⠉⠸⠫⠼⠢⠴⠻"] # 0x32bf + +#NOTE: uncertainty here + - "Ⓐ-Ⓩ": # 0x24b6 - 0x24cf + - tc: "⠫⠉⠸⠫⠠" + - spell: "translate('.', 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - tc: "⠻" + + - "ⓐ-ⓩ": # 0x24d0 - 0x24e9 + - tc: "⠫⠉⠸⠫" + - spell: "translate('.', 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ', 'abcdefghijklmnopqrstuvwxyz')" + - tc: "⠻" + + # --- Nemeth Default Alphabet circled-sans-serif chars. --- + + - "➀-➈": # 0x1f10b, 0x2780 - 0x2788 + - tc: "⠫⠉⠸⠫⠠⠨⠼" + - spell: "translate('.', '➀➁➂➃➄➅➆➇➈', '123456789')" + - tc: "⠻" + + - "🄋": [tc: "⠫⠉⠸⠫⠠⠨⠼⠴⠻"] # 0x1f10b + - "➉": [tc: "⠫⠉⠸⠫⠠⠨⠼⠂⠴⠻"] # 0x2789 + + # --- Nemeth Default Alphabet comma chars. --- + + - "🄁-🄊": # 0x1f101 - 0x1f10a + - spell: "translate('.', '🄁🄂🄃🄄🄅🄆🄇🄈🄉🄊', '0123456789')" + - tc: "⠠" + + + # --- Nemeth Default Alphabet double-circled chars. --- + + - "⓵": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠂⠻⠻"] # 0x24f5 + - "⓶": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠆⠻⠻"] # 0x24f6 + - "⓷": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠒⠻⠻"] # 0x24f7 + - "⓸": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠲⠻⠻"] # 0x24f8 + - "⓹": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠢⠻⠻"] # 0x24f9 + - "⓺": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠖⠻⠻"] # 0x24fa + - "⓻": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠶⠻⠻"] # 0x24fb + - "⓼": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠦⠻⠻"] # 0x24fc + - "⓽": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠔⠻⠻"] # 0x24fd + - "⓾": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠂⠴⠻⠻"] # 0x24fe + + # --- Nemeth Default Alphabet double-struck chars. --- + - "𝔸-𝕐": # 0x1d538 - 0x1d550 (partial, excludes ℂ ℍ ℕ ℙ ℚ ℝ ℤ) + - tc: "𝔹E" + - spell: "translate('.', '𝔸𝔹𝔻𝔼𝔽𝔾𝕀𝕁𝕂𝕃𝕄𝕆𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'ABDEFGIJKLMOSTUVWXY')" + + - "ℂ": [tc: "𝔹ECL⠉"] # 0x2102 + - "ℍ": [tc: "𝔹ECL⠓"] # 0x210d + - "ℕ": [tc: "𝔹ECL⠝"] # 0x2115 + - "ℙ": [tc: "𝔹ECL⠏"] # 0x2119 + - "ℚ": [tc: "𝔹ECL⠟"] # 0x211a + - "ℝ": [tc: "𝔹ECL⠗"] # 0x211d + - "ℤ": [tc: "𝔹ECL⠵"] # 0x2124 + - "Ω": [t: "CGL⠺"] # 0x2126 (Ohm sign (capital Greek omega)) + + - "𝕒-𝕫": # 0x1d552 - 0x1d56b + - tc: "𝔹E" + - spell: "translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝟘-𝟡": # 0x1d7d8 - 0x1d7e1 + - tc: "𝔹" + - spell: "translate('.', '𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡', '0123456789')" + + + # --- Nemeth Default Alphabet fraktur chars. --- + + - "𝔄-𝔜": # 0x1d504 - 0x1d51c + - tc: "D" + - spell: "translate('.', '𝔄𝔅𝔇𝔈𝔉𝔊𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔖𝔗𝔘𝔙𝔚𝔛𝔜', 'ABDEFGJKLMNOPQSTUVWXY')" + + - "ℭ": [tc: "DCL⠉"] # 0x212d + - "ℌ": [tc: "DCL⠓"] # 0x210c + - "ℑ": [tc: "DCL⠊"] # 0x2111 + - "ℜ": [tc: "DCL⠗"] # 0x211c + - "ℨ": [tc: "DCL⠵"] # 0x2128 + + - "𝔞-𝔷": # 0x1d51e - 0x1d537 + - tc: "D" + - spell: "translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz')" + + # --- Nemeth Default Alphabet fullwidth chars. --- + + - "A-Z": # 0xff21 - 0xff3a + - tc: "" + - spell: "translate('.', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "a-z": # 0xff41 - 0xff5a + - tc: "" + - spell: "translate('.', 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz')" + + - "0-9": # 0xff10 - 0xff19 + - tc: "" + - spell: "translate('.', '0123456789', '0123456789')" + + # --- Nemeth Default Alphabet italic chars -- treat as non-italic. --- + + - "𝐴-𝑍": # 0x1d434 - 0x1d44d + - tc: "E" + - spell: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝑎-𝑧": # 0x1d44e - 0x1d467 + - tc: "E" + - spell: "translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔ℎ𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝛢-𝛺": # 0x1d6e2 - 0x1d6fa + - tc: "G" + - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹𝛺', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝛻": [tc: "GL⠫"] # 0x1d6fb + + - "𝛼-𝜌": # 0x1d6fc - 0x1d70c + - tc: "G" + - spell: "translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌', 'αβγδεζηθικλμνξοπρ')" + - "𝜍": [tc: "VL⠎"] # 0x1d70d + + - "𝜎": [tc: "GL⠎"] # 0x1d70e + - "𝜏": [tc: "GL⠞"] # 0x1d70f + - "𝜐": [tc: "GL⠥"] # 0x1d710 + + - "𝜑": [tc: "VL⠋"] # 0x1d711 + + - "𝜒": [tc: "GL⠯"] # 0x1d712 + - "𝜓": [tc: "GL⠽"] # 0x1d713 + - "𝜔": [tc: "GL⠺"] # 0x1d714 + + - "𝜕": [tc: "VL⠙"] # 0x1d715 + + - "𝜖": [tc: "GL⠑"] # 0x1d716 + + - "𝜗": [tc: "VL⠹"] # 0x1d717 + + - "𝜘": [tc: "GL⠅"] # 0x1d718 + - "𝜙": [tc: "GL⠋"] # 0x1d719 + - "𝜚": [tc: "GL⠗"] # 0x1d71a + - "𝜛": [tc: "GL⠏"] # 0x1d71b + + # --- Nemeth Default Alphabet monospace chars. --- + + - "𝙰-𝚉": # 0x1d670 - 0x1d689 + - tc: "" + - spell: "translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝚊-𝚣": # 0x1d68a - 0x1d6a3 + - tc: "" + - spell: "translate('.', '𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝟶-𝟿": # 0x1d7f6 - 0x1d7ff + - tc: "" + - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" + + + # --- Nemeth Default Alphabet negative-circled chars. --- + - "⓿": [t: "⠫⠸⠉⠸⠫⠼⠴⠻"] # 0x24ff + - "❶": [t: "⠫⠸⠉⠸⠫⠼⠂⠻"] # 0x2776 + - "❷": [t: "⠫⠸⠉⠸⠫⠼⠆⠻"] # 0x2777 + - "❸": [t: "⠫⠸⠉⠸⠫⠼⠒⠻"] # 0x2778 + - "❹": [t: "⠫⠸⠉⠸⠫⠼⠲⠻"] # 0x2779 + - "❺": [t: "⠫⠸⠉⠸⠫⠼⠢⠻"] # 0x277a + - "❻": [t: "⠫⠸⠉⠸⠫⠼⠖⠻"] # 0x277b + - "❼": [t: "⠫⠸⠉⠸⠫⠼⠶⠻"] # 0x277c + - "❽": [t: "⠫⠸⠉⠸⠫⠼⠦⠻"] # 0x277d + - "❾": [t: "⠫⠸⠉⠸⠫⠼⠔⠻"] # 0x277e + - "❿": [t: "⠫⠸⠉⠸⠫⠼⠂⠴⠻"] # 0x277f + - "⓫": [t: "⠫⠸⠉⠸⠫⠼⠂⠂⠻"] # 0x24eb + - "⓬": [t: "⠫⠸⠉⠸⠫⠼⠂⠆⠻"] # 0x24ec + - "⓭": [t: "⠫⠸⠉⠸⠫⠼⠂⠒⠻"] # 0x24ed + - "⓮": [t: "⠫⠸⠉⠸⠫⠼⠂⠲⠻"] # 0x24ee + - "⓯": [t: "⠫⠸⠉⠸⠫⠼⠂⠢⠻"] # 0x24ef + - "⓰": [t: "⠫⠸⠉⠸⠫⠼⠂⠖⠻"] # 0x24f0 + - "⓱": [t: "⠫⠸⠉⠸⠫⠼⠂⠶⠻"] # 0x24f1 + - "⓲": [t: "⠫⠸⠉⠸⠫⠼⠂⠦⠻"] # 0x24f2 + - "⓳": [t: "⠫⠸⠉⠸⠫⠼⠂⠔⠻"] # 0x24f3 + - "⓴": [t: "⠫⠸⠉⠸⠫⠼⠆⠴⠻"] # 0x24f4 + + - "🅐": [t: "⠫⠸⠉⠸⠫⠠⠁⠻"] # 0x1f150 + - "🅑": [t: "⠫⠸⠉⠸⠫⠠⠃⠻"] # 0x1f151 + - "🅒": [t: "⠫⠸⠉⠸⠫⠠⠉⠻"] # 0x1f152 + - "🅓": [t: "⠫⠸⠉⠸⠫⠠⠙⠻"] # 0x1f153 + - "🅔": [t: "⠫⠸⠉⠸⠫⠠⠑⠻"] # 0x1f154 + - "🅕": [t: "⠫⠸⠉⠸⠫⠠⠋⠻"] # 0x1f155 + - "🅖": [t: "⠫⠸⠉⠸⠫⠠⠛⠻"] # 0x1f156 + - "🅗": [t: "⠫⠸⠉⠸⠫⠠⠓⠻"] # 0x1f157 + - "🅘": [t: "⠫⠸⠉⠸⠫⠠⠊⠻"] # 0x1f158 + - "🅙": [t: "⠫⠸⠉⠸⠫⠠⠚⠻"] # 0x1f159 + - "🅚": [t: "⠫⠸⠉⠸⠫⠠⠅⠻"] # 0x1f15a + - "🅛": [t: "⠫⠸⠉⠸⠫⠠⠇⠻"] # 0x1f15b + - "🅜": [t: "⠫⠸⠉⠸⠫⠠⠍⠻"] # 0x1f15c + - "🅝": [t: "⠫⠸⠉⠸⠫⠠⠝⠻"] # 0x1f15d + - "🅞": [t: "⠫⠸⠉⠸⠫⠠⠕⠻"] # 0x1f15e + - "🅟": [t: "⠫⠸⠉⠸⠫⠠⠏⠻"] # 0x1f15f + - "🅠": [t: "⠫⠸⠉⠸⠫⠠⠟⠻"] # 0x1f160 + - "🅡": [t: "⠫⠸⠉⠸⠫⠠⠗⠻"] # 0x1f161 + - "🅢": [t: "⠫⠸⠉⠸⠫⠠⠎⠻"] # 0x1f162 + - "🅣": [t: "⠫⠸⠉⠸⠫⠠⠞⠻"] # 0x1f163 + - "🅤": [t: "⠫⠸⠉⠸⠫⠠⠥⠻"] # 0x1f164 + - "🅥": [t: "⠫⠸⠉⠸⠫⠠⠧⠻"] # 0x1f165 + - "🅦": [t: "⠫⠸⠉⠸⠫⠠⠺⠻"] # 0x1f166 + - "🅧": [t: "⠫⠸⠉⠸⠫⠠⠭⠻"] # 0x1f167 + - "🅨": [t: "⠫⠸⠉⠸⠫⠠⠽⠻"] # 0x1f168 + - "🅩": [t: "⠫⠸⠉⠸⠫⠠⠵⠻"] # 0x1f169 + + # --- Nemeth Default Alphabet negative-circled-sans-serif chars. --- + - "🄌": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠴⠻"] # 0x1f10c + - "➊": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠂⠻"] # 0x278a + - "➋": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠆⠻"] # 0x278b + - "➌": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠒⠻"] # 0x278c + - "➍": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠲⠻"] # 0x278d + - "➎": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠢⠻"] # 0x278e + - "➏": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠖⠻"] # 0x278f + - "➐": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠶⠻"] # 0x2790 + - "➑": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠦⠻"] # 0x2791 + - "➒": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠔⠻"] # 0x2792 + - "➓": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠂⠴⠻"] # 0x2793 + + # --- Nemeth Default Alphabet negative-squared chars. --- + - "🅰-🆉": # 0x1f170 - 0x1f189 + - tc: "⠫⠸⠲⠸⠫⠠" + - spell: "translate('.', '🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - tc: "⠻" + + # --- Nemeth Default Alphabet parenthesized chars. --- + + - "⑴-⑼": # 0x2474 - 0x247c + - tc: "⠷" + - spell: "translate('.', '⑴⑵⑶⑷⑸⑹⑺⑻⑼', '123456789')" + - tc: "⠾" + + - "⑽": [t: "⠷⠂⠴⠾"] # 0x247d + - "⑾": [t: "⠷⠂⠂⠾"] # 0x247e + - "⑿": [t: "⠷⠂⠆⠾"] # 0x247f + - "⒀": [t: "⠷⠂⠒⠾"] # 0x2480 + - "⒁": [t: "⠷⠂⠲⠾"] # 0x2481 + - "⒂": [t: "⠷⠂⠢⠾"] # 0x2482 + - "⒃": [t: "⠷⠂⠖⠾"] # 0x2483 + - "⒄": [t: "⠷⠂⠶⠾"] # 0x2484 + - "⒅": [t: "⠷⠂⠦⠾"] # 0x2485 + - "⒆": [t: "⠷⠂⠔⠾"] # 0x2486 + - "⒇": [t: "⠷⠆⠴⠾"] # 0x2487 + + - "🄐-🄩": # 0x1f110 - 0x1f129 + - tc: "⠷⠠" + - spell: "translate('.', '🄐🄑🄒🄓🄔🄕🄖🄗🄘🄙🄚🄛🄜🄝🄞🄟🄠🄡🄢🄣🄤🄥🄦🄧🄨🄩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - tc: "⠾" + + - "⒜-⒵": # 0x249c - 0x24b5 + - tc: "⠷" + - spell: "translate('.', '⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵', 'abcdefghijklmnopqrstuvwxyz')" + - tc: "⠾" + + # --- Nemeth Default Alphabet period chars. --- + - "🄀": [t: "⠴⠸⠲"] # 0x1f100 + - "⒈": [t: "⠂⠸⠲"] # 0x2488 + - "⒉": [t: "⠆⠸⠲"] # 0x2489 + - "⒊": [t: "⠒⠸⠲"] # 0x248a + - "⒋": [t: "⠲⠸⠲"] # 0x248b + - "⒌": [t: "⠢⠸⠲"] # 0x248c + - "⒍": [t: "⠖⠸⠲"] # 0x248d + - "⒎": [t: "⠶⠸⠲"] # 0x248e + - "⒏": [t: "⠦⠸⠲"] # 0x248f + - "⒐": [t: "⠔⠸⠲"] # 0x2490 + + - "⒑": [t: "⠂⠴⠸⠲"] # 0x2491 + - "⒒": [t: "⠂⠂⠸⠲"] # 0x2492 + - "⒓": [t: "⠂⠆⠸⠲"] # 0x2493 + - "⒔": [t: "⠂⠒⠸⠲"] # 0x2494 + - "⒕": [t: "⠂⠲⠸⠲"] # 0x2495 + - "⒖": [t: "⠂⠢⠸⠲"] # 0x2496 + - "⒗": [t: "⠂⠖⠸⠲"] # 0x2497 + - "⒘": [t: "⠂⠶⠸⠲"] # 0x2498 + - "⒙": [t: "⠂⠦⠸⠲"] # 0x2499 + - "⒚": [t: "⠂⠔⠸⠲"] # 0x249a + - "⒛": [t: "⠆⠴⠸⠲"] # 0x249b + + # --- Nemeth Default Alphabet sans-serif chars. --- + - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 + - tc: "SE" + - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝖺-𝗓": # 0x1d5ba - 0x1d5d3 + - tc: "SE" + - spell: "translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝟢-𝟫": # 0x1d7e2 - 0x1d7eb + - tc: "S" + - spell: "translate('.', '𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫', '0123456789')" + + # --- Nemeth Default Alphabet sans-serif-bold chars. --- + - "𝗔-𝗭": # 0x1d5d4 - 0x1d5ed + - tc: "SBE" + - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝗮-𝘇": # 0x1d5ee - 0x1d607 + - tc: "SBE" + - spell: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝝖-𝝮": # 0x1d756 - 0x1d76e + - tc: "SBG" + - spell: "translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭𝝮', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝝰-𝞀": # 0x1d770 - 0x1d780 + - tc: "SBG" + - spell: "translate('.', '𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿𝞀', 'αβγδεζηθικλμνξοπρ')" + + - "𝝯": [tc: "SBGL⠫"] # 0x1d76f + + - "𝞁": [tc: "SBVL⠎"] # 0x1d781 + + - "𝞂-𝞄": # 0x1d782 - 0x1d784 + - tc: "SBG" + - spell: "translate('.', '𝞂𝞃𝞄', 'στυ')" + + - "𝞅": [tc: "SBVL⠋"] # 0x1d785 + + - "𝞆-𝞈": # 0x1d786 - 0x1d788 + - tc: "SBG" + - spell: "translate('.', '𝞆𝞇𝞈', 'χψω')" + + - "𝞉": [tc: "⠠⠨⠸⠈⠙"] # 0x1d789 + - "𝞊": [tc: "SBGL⠑"] # 0x1d78a + - "𝞋": [tc: "SBVL⠹"] # 0x1d78b + + - "𝞌-𝞏": # 0x1d78c - 0x1d78f + - tc: "SBG" + - spell: "translate('.', '𝞌𝞍𝞎𝞏', 'κφρπ')" + + - "𝟬-𝟵": # 0x1d7ec - 0x1d7f5 + - tc: "SB" + - spell: "translate('.', '𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵', '0123456789')" + + # --- Nemeth Default Alphabet sans-serif-bold-italic chars. --- + + - "𝘼-𝙕": # 0x1d63c - 0x1d655 + - tc: "SBE" + - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝙖-𝙯": # 0x1d656 - 0x1d66f + - tc: "SBE" + - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝞐-𝞨": # 0x1d790 - 0x1d7a8 + - tc: "SBG" + - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝞩": [t: "SBGL⠫"] # 0x1d7a9 + + - "𝞪-𝞺": # 0x1d7aa - 0x1d7ba + - tc: "SBG" + - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺', 'αβγδεζηθικλμνξοπρ')" + + - "𝞻": [t: "SBVL⠎"] # 0x1d7bb + - "𝞼": [t: "SBGL⠎"] # 0x1d7bc + - "𝞽": [t: "SBGL⠞"] # 0x1d7bd + - "𝞾": [t: "SBGL⠥"] # 0x1d7be + - "𝞿": [t: "SBVL⠋"] # 0x1d7bf + + - "𝟀": [t: "SBGL⠯"] # 0x1d7c0 + - "𝟁": [t: "SBGL⠽"] # 0x1d7c1 + - "𝟂": [t: "SBGL⠺"] # 0x1d7c2 + - "𝟃": [t: "SBVL⠙"] # 0x1d7c3 + + - "𝟄": [t: "SBGL⠑"] # 0x1d7c4 + - "𝟅": [t: "SBVL⠹"] # 0x1d7c5 + + - "𝟆": [t: "SBGL⠅"] # 0x1d7c6 + - "𝟇": [t: "SBGL⠋"] # 0x1d7c7 + - "𝟈": [t: "SBGL⠗"] # 0x1d7c8 + - "𝟉": [t: "SBGL⠏"] # 0x1d7c9 + + # --- Nemeth Default Alphabet sans-serif-italic chars. --- + - "𝘈-𝘡": # 0x1d608 - 0x1d621 + - tc: "SE" + - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝘢-𝘻": # 0x1d622 - 0x1d63b + - tc: "SE" + - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" + + # --- Nemeth Default Alphabet script chars. --- + - "𝒜-𝒵": # 0x1d49c - 0x1d4b5 + - tc: "TE" + - spell: "translate('.', '𝒜𝒞𝒟𝒢𝒥𝒦𝒩𝒪𝒫𝒬𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'ACDGJKNOPQSTUVWXYZ')" + + - "ℬ": [t: "TECL⠃"] # 0x212c + - "ℰ": [t: "TECL⠑"] # 0x2130 + - "ℱ": [t: "TECL⠋"] # 0x2131 + - "ℋ": [t: "TECL⠓"] # 0x210b + - "ℐ": [t: "TECL⠊"] # 0x2110 + - "ℒ": [t: "TECL⠇"] # 0x2112 + - "ℳ": [t: "TECL⠍"] # 0x2133 + - "ℛ": [t: "TECL⠗"] # 0x211b + + - "𝒶-𝓏": # 0x1d4b6 - 0x1d4cf + - tc: "TE" + - spell: "translate('.', '𝒶𝒷𝒸𝒹𝒻𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏', 'abcdfhijklmnpqrstuvwxyz')" + + - "ℯ": [t: "TEL⠑"] # 0x212f + - "ℊ": [t: "TEL⠛"] # 0x210a + - "ℴ": [t: "TEL⠕"] # 0x2134 + + # --- Nemeth Default Alphabet squared chars. --- + + - "🄰": [t: "⠫⠲⠸⠫⠠⠁⠻"] # 0x1f130 + - "🄱": [t: "⠫⠲⠸⠫⠠⠃⠻"] # 0x1f131 + - "🄲": [t: "⠫⠲⠸⠫⠠⠉⠻"] # 0x1f132 + - "🄳": [t: "⠫⠲⠸⠫⠠⠙⠻"] # 0x1f133 + - "🄴": [t: "⠫⠲⠸⠫⠠⠑⠻"] # 0x1f134 + - "🄵": [t: "⠫⠲⠸⠫⠠⠋⠻"] # 0x1f135 + - "🄶": [t: "⠫⠲⠸⠫⠠⠛⠻"] # 0x1f136 + - "🄷": [t: "⠫⠲⠸⠫⠠⠓⠻"] # 0x1f137 + - "🄸": [t: "⠫⠲⠸⠫⠠⠊⠻"] # 0x1f138 + - "🄹": [t: "⠫⠲⠸⠫⠠⠚⠻"] # 0x1f139 + - "🄺": [t: "⠫⠲⠸⠫⠠⠅⠻"] # 0x1f13a + - "🄻": [t: "⠫⠲⠸⠫⠠⠇⠻"] # 0x1f13b + - "🄼": [t: "⠫⠲⠸⠫⠠⠍⠻"] # 0x1f13c + - "🄽": [t: "⠫⠲⠸⠫⠠⠝⠻"] # 0x1f13d + - "🄾": [t: "⠫⠲⠸⠫⠠⠕⠻"] # 0x1f13e + - "🄿": [t: "⠫⠲⠸⠫⠠⠏⠻"] # 0x1f13f + - "🅀": [t: "⠫⠲⠸⠫⠠⠟⠻"] # 0x1f140 + - "🅁": [t: "⠫⠲⠸⠫⠠⠗⠻"] # 0x1f141 + - "🅂": [t: "⠫⠲⠸⠫⠠⠎⠻"] # 0x1f142 + - "🅃": [t: "⠫⠲⠸⠫⠠⠞⠻"] # 0x1f143 + - "🅄": [t: "⠫⠲⠸⠫⠠⠥⠻"] # 0x1f144 + - "🅅": [t: "⠫⠲⠸⠫⠠⠧⠻"] # 0x1f145 + - "🅆": [t: "⠫⠲⠸⠫⠠⠺⠻"] # 0x1f146 + - "🅇": [t: "⠫⠲⠸⠫⠠⠭⠻"] # 0x1f147 + - "🅈": [t: "⠫⠲⠸⠫⠠⠽⠻"] # 0x1f148 + - "🅉": [t: "⠫⠲⠸⠫⠠⠵⠻"] # 0x1f149 + + # --- Nemeth Default Alphabet sub chars. --- + + - "₀": [t: "⠰⠴"] # 0x2080 + - "₁": [t: "⠰⠂"] # 0x2081 + - "₂": [t: "⠰⠆"] # 0x2082 + - "₃": [t: "⠰⠒"] # 0x2083 + - "₄": [t: "⠰⠲"] # 0x2084 + - "₅": [t: "⠰⠢"] # 0x2085 + - "₆": [t: "⠰⠖"] # 0x2086 + - "₇": [t: "⠰⠶"] # 0x2087 + - "₈": [t: "⠰⠦"] # 0x2088 + - "₉": [t: "⠰⠔"] # 0x2089 + + # --- Nemeth Default Alphabet super chars. --- + + - "⁰": [t: "⠘⠴"] # 0x2070 + - "¹": [t: "⠘⠂"] # 0xb9 + - "²": [t: "⠘⠆"] # 0xb2 + - "³": [t: "⠘⠒"] # 0xb3 + - "⁴": [t: "⠘⠲"] # 0x2074 + - "⁵": [t: "⠘⠢"] # 0x2075 + - "⁶": [t: "⠘⠖"] # 0x2076 + - "⁷": [t: "⠘⠶"] # 0x2077 + - "⁸": [t: "⠘⠦"] # 0x2078 + - "⁹": [t: "⠘⠔"] # 0x2079 + + # --- Nemeth Default Characters latin-lower-phonetic chars. --- + + - "ø": [t: "⠈⠕"] # 0xf8 + + # --- Nemeth Default Characters math_geometry chars. --- + + - "─": [t: "⠱"] # 0x2500 + - "│": [t: "⠳"] # 0x2502 + - "▰": [t: "⠫⠸⠛"] # 0x25b0 + - "▱": [t: "⠫⠛"] # 0x25b1 + - "▽": [t: "⠨⠫"] # 0x25bd + - "◆": [t: "⠫⠸⠙"] # 0x25c6 + - "◇": [t: "⠫⠙"] # 0x25c7 + - "◈": [t: "⠫⠸⠙"] # 0x25c8 + - "◉": [t: "⠫⠸⠉"] # 0x25c9 + - "◊": [t: "⠫⠙"] # 0x25ca + - "⬟": [t: "⠫⠸⠢"] # 0x2b1f + - "⬠": [t: "⠫⠢"] # 0x2b20 + - "⬡": [t: "⠫⠖"] # 0x2b21 + - "⬢": [t: "⠫⠸⠖"] # 0x2b22 + - "⬣": [t: "⠫⠸⠖"] # 0x2b23 + - "⬤": [t: "⠫⠸⠉"] # 0x2b24 + - "⬥": [t: "⠫⠸⠙"] # 0x2b25 + + # --- Nemeth Default Characters math_symbols chars. --- + + - "\"": [t: "⠄⠄"] # 0x22 + - "¡": [t: "⠖"] # 0xa1 + - "¢": [t: "⠈⠉"] # 0xa2 + - "£": [t: "⠈⠇"] # 0xa3 + - "¤": [t: "⠈⠫"] # 0xa4 + - "¥": [t: "⠈⠽"] # 0xa5 + - "§": [t: "⠈⠠⠎"] # 0xa7 + - "©": [t: "⠘⠉"] # 0xa9 (Copyright) + - "ª": [t: "⠸⠰⠁"] # 0xaa + - "«": [t: "⠐⠅⠈⠐⠅⠻"] # 0xab + - "®": [t: "⠘⠗"] # 0xae + - "´": [t: "⠈"] # 0xb4 + - "µ": [t: "⠨⠍"] # 0xb5 + - "¶": [t: "⠈⠠⠏"] # 0xb6 + - "¿": [t: "⠦"] # 0xbf + - "ʹ": [t: "⠄"] # 0x2b9 + - "ʺ": [t: "⠄⠄"] # 0x2ba + - "˙": [t: "⠡"] # 0x2d9 + - "˜": [t: "⠈⠱"] # 0x2dc + - "‐": [t: "⠤"] # 0x2010 + - "‑": [t: "⠤"] # 0x2011 + - "‒": [t: "⠤⠤"] # 0x2012 + - "–": [t: "⠤⠤"] # 0x2013 + - "‖": [t: "⠳⠳"] # 0x2016 + - "†": [t: "⠸⠻"] # 0x2020 + - "‡": [t: "⠸⠸⠻"] # 0x2021 + - "•": [t: "⠡"] # 0x2022 + - "․": [t: "⠄"] # 0x2024 + - "‥": [t: "⠄⠄"] # 0x2025 + - "′": [t: "⠄"] # 0x2032 + - "″": [t: "⠄⠄"] # 0x2033 + - "‴": [t: "⠄⠄."] # 0x2034 (Triple prime) + - "⁗": [t: "⠄⠄.."] # 0x2057 (Quadruple prime) + - "‼": [t: "⠖⠖"] # 0x203c + - "‾": [t: "⠱"] # 0x203e + - "⁇": [t: "⠹⠹"] # 0x2047 + - "⁈": [t: "⠹⠖"] # 0x2048 + - "⁉": [t: "⠖⠹"] # 0x2049 + - "∀": [t: "⠈⠯"] # 0x2200 + - "∃": [t: "⠈⠿"] # 0x2203 + - "∄": [t: "⠌⠈⠿"] # 0x2204 + - "∅": [t: "⠸⠴"] # 0x2205 + - "∈": [t: "⠈⠑"] # 0x2208 + - "∉": [t: "⠌⠈⠑"] # 0x2209 + - "∊": [t: "⠈⠑"] # 0x220a + - "∋": [t: "⠈⠢"] # 0x220b + - "∌": [t: "⠌⠈⠢"] # 0x220c + - "∍": [t: "⠈⠢"] # 0x220d + - "∎": [t: "⠸⠳"] # 0x220e + - "−": [t: "⠤"] # 0x2212 + - "∓": [t: "⠤⠬"] # 0x2213 + - "∕": [t: "⠸⠌"] # 0x2215 + - "∖": [t: "⠸⠡"] # 0x2216 + - "∗": [t: "⠈⠼"] # 0x2217 + - "∘": [t: "⠨⠡"] # 0x2218 + - "∝": [t: "⠸⠿"] # 0x221d + - "∞": [t: "⠠⠿"] # 0x221e + - "∟": [t: "⠫⠪⠨⠗⠻"] # 0x221f + - "∠": [t: "⠫⠪"] # 0x2220 + - "∢": [t: "⠫⠪⠸⠫⠫⠁⠻"] # 0x2222 + - "∣": [t: "⠳"] # 0x2223 + - "∤": [t: "⠌⠳"] # 0x2224 + - "∥": [t: "⠫⠇"] # 0x2225 + - "∦": [t: "⠌⠫⠇"] # 0x2226 + - "∧": [t: "⠈⠩"] # 0x2227 + - "∨": [t: "⠈⠬"] # 0x2228 + - "∩": [t: "⠨⠩"] # 0x2229 + - "∪": [t: "⠨⠬"] # 0x222a + - "∫": [t: "⠮"] # 0x222b + - "∬": [t: "⠮⠮"] # 0x222c + - "∭": [t: "⠮⠮⠮"] # 0x222d + - "∮": [t: "⠮⠈⠫⠉⠻"] # 0x222e + - "∲": [t: "⠮⠈⠫⠪⠢⠔⠻"] # 0x2232 + - "∳": [t: "⠮⠈⠫⠢⠔⠕⠻"] # 0x2233 + - "∴": [t: "⠠⠡"] # 0x2234 + - "∵": [t: "⠈⠌"] # 0x2235 + - "∶": [t: "⠐⠂"] # 0x2236 + - "∷": [t: "⠰⠆"] # 0x2237 + - "∸": [t: "⠨⠤"] # 0x2238 + - "∼": [t: "⠈⠱"] # 0x223c + - "≁": [t: "⠌⠈⠱"] # 0x2241 + - "≂": [t: "⠱⠈⠱"] # 0x2242 + - "≃": [t: "⠈⠱⠱"] # 0x2243 + - "≄": [t: "⠌⠈⠱⠱"] # 0x2244 + - "≅": [t: "⠈⠱⠨⠅"] # 0x2245 + - "≆": [t: "⠈⠱⠌⠨⠅"] # 0x2246 + - "≇": [t: "⠌⠈⠱⠨⠅"] # 0x2247 + - "≈": [t: "⠈⠱⠈⠱"] # 0x2248 + - "≉": [t: "⠌⠈⠱⠈⠱"] # 0x2249 + - "≊": [t: "⠈⠱⠈⠱⠱"] # 0x224a + - "≍": [t: "⠈⠣⠠⠣"] # 0x224d + - "≎": [t: "⠈⠣⠠⠣"] # 0x224e + - "≐": [t: "⠐⠨⠅⠣⠡⠻"] # 0x2250 + - "≑": [t: "⠐⠨⠅⠩⠡⠣⠡⠻"] # 0x2251 + - "≗": [t: "⠐⠨⠅⠣⠨⠡⠻"] # 0x2257 + - "≘": [t: "⠐⠨⠅⠣⠫⠁⠻"] # 0x2258 + - "≙": [t: "⠐⠨⠅⠣⠸⠣⠻"] # 0x2259 + - "≚": [t: "⠐⠨⠅⠣⠸⠩⠻"] # 0x225a + - "≛": [t: "⠐⠨⠅⠣⠫⠸⠎⠻"] # 0x225b + - "≜": [t: "⠐⠨⠅⠣⠫⠞⠻"] # 0x225c + - "≝": [t: "⠐⠨⠅⠣⠙⠑⠋⠻"] # 0x225d + - "≞": [t: "⠐⠨⠅⠣⠍⠻"] # 0x225e + - "≟": [t: "⠐⠨⠅⠣⠸⠦⠻"] # 0x225f + - "≠": [t: "⠌⠨⠅"] # 0x2260 + - "≢": [t: "⠌⠸⠇"] # 0x2262 + - "≤": [t: "⠐⠅⠱"] # 0x2264 + - "≥": [t: "⠨⠂⠱"] # 0x2265 + - "≦": [t: "⠐⠅⠨⠅"] # 0x2266 + - "≧": [t: "⠨⠂⠨⠅"] # 0x2267 + - "≨": [t: "⠐⠅⠌⠨⠅"] # 0x2268 + - "≩": [t: "⠨⠂⠌⠨⠅"] # 0x2269 + - "≪": [t: "⠐⠅⠈⠐⠅⠻"] # 0x226a + - "≫": [t: "⠨⠂⠈⠨⠂⠻"] # 0x226b + - "≭": [t: "⠌⠈⠣⠠⠣"] # 0x226d + - "≮": [t: "⠌⠐⠅"] # 0x226e + - "≯": [t: "⠌⠨⠂"] # 0x226f + - "≰": [t: "⠌⠐⠅⠱"] # 0x2270 + - "≱": [t: "⠌⠨⠂⠱"] # 0x2271 + - "≲": [t: "⠐⠅⠈⠱"] # 0x2272 + - "≳": [t: "⠨⠂⠈⠱"] # 0x2273 + - "≴": [t: "⠌⠐⠅⠈⠱"] # 0x2274 + - "≵": [t: "⠌⠨⠂⠈⠱"] # 0x2275 + - "≶": [t: "⠐⠅⠨⠂"] # 0x2276 + - "≷": [t: "⠨⠂⠐⠅"] # 0x2277 + - "≸": [t: "⠌⠐⠅⠨⠂"] # 0x2278 + - "≹": [t: "⠌⠨⠂⠐⠅"] # 0x2279 + - "≺": [t: "⠨⠐⠅"] # 0x227a + - "≻": [t: "⠨⠨⠂"] # 0x227b + - "≼": [t: "⠨⠐⠅⠱"] # 0x227c + - "≽": [t: "⠨⠨⠐⠱"] # 0x227d + - "≾": [t: "⠨⠐⠅⠈⠱"] # 0x227e + - "≿": [t: "⠨⠨⠂⠈⠱"] # 0x227f + - "⊀": [t: "⠌⠨⠐⠅"] # 0x2280 + - "⊁": [t: "⠌⠨⠐⠅"] # 0x2281 + - "⊂": [t: "⠸⠐⠅"] # 0x2282 + - "⊃": [t: "⠸⠨⠂"] # 0x2283 + - "⊄": [t: "⠌⠸⠐⠅"] # 0x2284 + - "⊅": [t: "⠌⠸⠨⠂"] # 0x2285 + - "⊆": [t: "⠸⠐⠅⠱"] # 0x2286 + - "⊇": [t: "⠸⠨⠂⠱"] # 0x2287 + - "⊈": [t: "⠌⠸⠐⠅⠱"] # 0x2288 + - "⊉": [t: "⠌⠸⠨⠂⠱"] # 0x2289 + - "⊊": [t: "⠸⠐⠅⠌⠨⠅"] # 0x228a + - "⊋": [t: "⠸⠨⠂⠌⠨⠅"] # 0x228b + - "⊥": [t: "⠫⠏"] # 0x22a5 + - "⋀": [t: "⠈⠩"] # 0x22c0 + - "⋁": [t: "⠈⠬"] # 0x22c1 + - "⋂": [t: "⠨⠩"] # 0x22c2 + - "⋃": [t: "⠨⠬"] # 0x22c3 + - "⋄": [t: "⠫⠩"] # 0x22c4 + - "⋜": [t: "⠱⠐⠅"] # 0x22dc + - "⋝": [t: "⠱⠨⠂"] # 0x22dd + - "⟂": [t: "⠫⠏"] # 0x27c2 + - "⦀": [t: "⠳⠳⠳"] # 0x2980 + - "⦶": [t: "⠫⠉⠸⠫⠳⠻"] # 0x29b6 + - "⦷": [t: "⠫⠉⠸⠫⠫⠇⠻"] # 0x29b7 + - "⦸": [t: "⠫⠉⠸⠫⠸⠡⠻"] # 0x29b8 + - "⦹": [t: "⠫⠉⠸⠫⠫⠏⠻"] # 0x29b9 + - "⦿": [t: "⠫⠉⠸⠫⠔⠔⠻"] # 0x29bf + - "⧀": [t: "⠫⠉⠸⠫⠐⠅⠻"] # 0x29c0 + - "⧁": [t: "⠫⠉⠸⠫⠨⠂⠻"] # 0x29c1 + - "⨀": [t: "⠫⠉⠸⠫⠡⠻"] # 0x2a00 + - "⨁": [t: "⠫⠉⠸⠫⠬⠻"] # 0x2a01 + - "⨂": [t: "⠫⠉⠸⠫⠈⠡⠻"] # 0x2a02 + - "⪯": [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: "⠈⠎"] # 0xfe69 + - "$": [t: "⠈⠎"] # 0xff04 + - "_": [t: "⠱"] # 0xff3f + - "~": [t: "⠈⠱"] # 0xff5e + - "¢": [t: "⠈⠉"] # 0xffe0 + - "£": [t: "⠈⠇"] # 0xffe1 + - " ̄": [t: "⠱"] # 0xffe3 + - "¥": [t: "⠈⠽"] # 0xffe5 + + # --- Nemeth Default Characters rest chars. --- + + - "ℵ": [t: "⠠⠠⠁"] # 0x2135 + - "ℶ": [t: "⠠⠠⠃"] # 0x2136 + - "ℷ": [t: "⠠⠠⠉"] # 0x2137 + - "ℸ": [t: "⠠⠠⠙"] # 0x2138 + - "à": [t: "⠈⠁"] # 0xe0 + - "á": [t: "⠈⠁"] # 0xe1 + - "â": [t: "⠈⠁"] # 0xe2 + - "ã": [t: "⠈⠁"] # 0xe3 + - "ä": [t: "⠈⠁"] # 0xe4 + - "å": [t: "⠈⠁"] # 0xe5 + - "ç": [t: "⠈⠉"] # 0xe7 + - "è": [t: "⠈⠑"] # 0xe8 + - "é": [t: "⠈⠑"] # 0xe9 + - "ê": [t: "⠈⠑"] # 0xea + - "ë": [t: "⠈⠑"] # 0xeb + - "ì": [t: "⠈⠊"] # 0xec + - "í": [t: "⠈⠊"] # 0xed + - "î": [t: "⠈⠊"] # 0xee + - "ï": [t: "⠈⠊"] # 0xef + - "ñ": [t: "⠈⠝"] # 0xf1 + - "ò": [t: "⠈⠕"] # 0xf2 + - "ó": [t: "⠈⠕"] # 0xf3 + - "ô": [t: "⠈⠕"] # 0xf4 + - "õ": [t: "⠈⠕"] # 0xf5 + - "ö": [t: "⠈⠕"] # 0xf6 + - "ù": [t: "⠈⠥"] # 0xf9 + - "ú": [t: "⠈⠥"] # 0xfa + - "û": [t: "⠈⠥"] # 0xfb + - "ü": [t: "⠈⠥"] # 0xfc + - "ý": [t: "⠈⠽"] # 0xfd + - "ÿ": [t: "⠈⠽"] # 0xff + + - "À": [t: "⠠⠈⠁"] # 0xc0 + - "Á": [t: "⠠⠈⠁"] # 0xc1 + - "Â": [t: "⠠⠈⠁"] # 0xc2 + - "Ã": [t: "⠠⠈⠁"] # 0xc3 + - "Ä": [t: "⠠⠈⠁"] # 0xc4 + - "Å": [t: "⠠⠈⠁"] # 0xc5 + - "Ç": [t: "⠠⠈⠉"] # 0xc7 + - "È": [t: "⠠⠈⠑"] # 0xc8 + - "É": [t: "⠠⠈⠑"] # 0xc9 + - "Ê": [t: "⠠⠈⠑"] # 0xca + - "Ë": [t: "⠠⠈⠑"] # 0xcb + - "Ì": [t: "⠠⠈⠊"] # 0xcc + - "Í": [t: "⠠⠈⠊"] # 0xcd + - "Î": [t: "⠠⠈⠊"] # 0xce + - "Ï": [t: "⠠⠈⠊"] # 0xcf + - "Ñ": [t: "⠠⠈⠝"] # 0xd1 + - "Ò": [t: "⠠⠈⠕"] # 0xd2 + - "Ó": [t: "⠠⠈⠕"] # 0xd3 + - "Ô": [t: "⠠⠈⠕"] # 0xd4 + - "Ö": [t: "⠠⠈⠕"] # 0xd6 + - "Ù": [t: "⠠⠈⠥"] # 0xd9 + - "Ú": [t: "⠠⠈⠥"] # 0xda + - "Û": [t: "⠠⠈⠥"] # 0xdb + - "Ü": [t: "⠠⠈⠥"] # 0xdc + - "Ý": [t: "⠠⠈⠽"] # 0xdd + - "←": [t: "⠫⠪⠒⠒"] # 0x2190 + + - "↑": # 0x2191 + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠣"] + else: [t: "⠫⠣⠒⠒⠕"] + + - "↓": # 0x2193 + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠩"] + else: [t: "⠫⠩⠒⠒⠕"] + + - "↔": [t: "⠫⠪⠒⠒⠕"] # 0x2194 + - "↕": [t: "⠫⠣⠩⠪⠒⠒⠕"] # 0x2195 + - "↖": [t: "⠫⠘⠪⠒⠒"] # 0x2196 + - "↗": [t: "⠫⠘⠒⠒⠕"] # 0x2197 + - "↘": [t: "⠫⠰⠒⠒⠕"] # 0x2198 + - "↙": [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: "⠫⠠⠳⠒⠒⠕"] # 0x21b4 + - "↵": [t: "⠫⠩⠠⠳⠒⠒⠕"] # 0x21b5 + - "↶": [t: "⠫⠢⠔⠕"] # 0x21b6 + - "↷": [t: "⠫⠪⠢⠔"] # 0x21b7 + - "↺": [t: "⠫⠢⠔⠕"] # 0x21ba + - "↻": [t: "⠫⠪⠢⠔"] # 0x21bb + - "⇄": [t: "⠫⠒⠒⠕⠫⠪⠒⠒"] # 0x21c4 + - "⇅": [t: "⠫⠣⠒⠒⠕⠐⠫⠩⠒⠒⠕"] # 0x21c5 + - "⇆": [t: "⠫⠪⠒⠒⠫⠒⠒⠕"] # 0x21c6 + - "⇇": [t: "⠫⠚⠒⠒⠫⠚⠒⠒"] # 0x21c7 + - "⇈": [t: "⠫⠣⠒⠒⠕⠐⠫⠣⠒⠒⠕"] # 0x21c8 + - "⇉": [t: "⠫⠒⠒⠕⠫⠒⠒⠕"] # 0x21c9 + - "⇊": [t: "⠫⠩⠒⠒⠕⠐⠫⠩⠒⠒⠕"] # 0x21ca + - "⇍": [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 FIX: how to represent triple shafts? + - "⇛": [t: "⠫⠶⠶⠕⠕"] # 0x21db FIX: how to represent triple shafts? + - "⇜": [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: "⠫⠣⠸⠪⠒⠒⠕"] # 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: "⠙"] # 0x2146 + - "ⅇ": [t: "⠑"] # 0x2147 + - "ⅈ": [t: "⠊"] # 0x2148 + - "⌜": [t: "⠈⠘⠷"] # 0x231c + - "⌝": [t: "⠈⠘⠾"] # 0x231d + - "⌞": [t: "⠈⠰⠷"] # 0x231e + - "⌟": [t: "⠈⠰⠾"] # 0x231f + - "〈": [t: "⠨⠨⠷"] # 0x2329 + - "〉": [t: "⠨⠨⠾"] # 0x232a + - "⏞": [t: "⠨⠷"] # 0x23de + - "⏟": [t: "⠨⠾"] # 0x23df + - "⎴": [t: "⠈⠷"] # 0x23b4 + - "⎵": [t: "⠈⠾"] # 0x23b5 + - "⟪": [t: "⠨⠨⠨⠷"] # 0x27ea + - "⟫": [t: "⠨⠨⠨⠾"] # 0x27eb + - "〈": [t: "⠨⠨⠷"] # 0x3008 + - "〉": [t: "⠨⠨⠾"] # 0x3009 + - "《": [t: "⠨⠨⠨⠷"] # 0x300a + - "》": [t: "⠨⠨⠨⠾"] # 0x300b + - "「": [t: "⠈⠘⠷"] # 0x300c + - "」": [t: "⠈⠘⠾"] # 0x300d + - "↼": [t: "⠫⠈⠪⠒⠒"] # 0x21bc + - "↽": [t: "⠫⠠⠪⠒⠒"] # 0x21bd + - "↾": [t: "⠫⠣⠒⠒⠠⠕"] # 0x21be + - "↿": [t: "⠫⠣⠒⠒⠈⠕"] # 0x21bf + - "⇀": [t: "⠫⠒⠒⠈⠕"] # 0x21c0 + - "⇁": [t: "⠫⠒⠒⠠⠕"] # 0x21c1 + - "⇂": [t: "⠫⠪⠒⠒⠈⠕"] # 0x21c2 + - "⇃": [t: "⠫⠩⠒⠒⠠⠕"] # 0x21c3 + - "⇋": # 0x21cb + + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠪⠒⠫⠒⠕"] + else: [t: "⠫⠈⠪⠒⠒⠫⠒⠒⠈⠕"] + - "⇌": # 0x21cc + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] + + - "℔": [t: "⠳"] # 0x2114 + - "™": [t: "⠘⠞"] # 0x2122 + - "Å": [t: "⠈⠠⠁"] # 0x212b + - "­": [t: "⠤"] # 0xad + - " -​": [t: "W"] # 0x2000 - 0x200b (various spaces / ZWSP) + - "
": [t: "W"] # 0x2028 + - "
": [t: "W"] # 0x2029 + - " ": [t: "W"] # 0x202f + - " ": [t: "W"] # 0x205f + - "₣": [t: "⠈⠋"] # 0x20A3 (French franc sign + - "₦": [t: "⠈⠝"] # 0x2046 (Naira sign) + - "₩": [t: "⠈⠺"] # 0x20A9 (Won sign) + - "€": [t: "⠈⠑"] # 0x20AC (Euro sign) + - "": [t: "W"] # 0xfeff + - "☆": [t: "⠫⠎"] # 0x2606 + - "✕": [t: "⠈⠡"] # 0x2715 + - "¼": [t: "⠹⠂⠌⠲⠼"] # 0xbc + - "½": [t: "⠹⠂⠌⠆⠼"] # 0xbd + - "¾": [t: "⠹⠒⠌⠲⠼"] # 0xbe + - "⅐": [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: "⠹⠴⠌⠒⠼"] # 0x2189 + - "㉈": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠂⠴⠻⠻"] # 0x3248 + - "㉉": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠆⠴⠻⠻"] # 0x3249 + - "㉊": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠒⠴⠻⠻"] # 0x324a + - "㉋": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠲⠴⠻⠻"] # 0x324b + - "㉌": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠢⠴⠻⠻"] # 0x324c + - "㉍": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠖⠴⠻⠻"] # 0x324d + - "㉎": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠶⠴⠻⠻"] # 0x324e + - "㉏": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠦⠴⠻⠻"] # 0x324f + + + - "‘": [t: "P⠠⠦"] # 0x2018 (Left single quotation mark) + - "’": [t: "P⠠⠴"] # 0x2019 (Right single quotation mark) + - "“": [t: "P⠦"] # 0x201C (Left double quotation mark) + - "”": [t: "P⠴"] # 0x201D (Right double quotation mark) + - "‰": [t: "`00"] + - "ℏ": [t: "⠈⠓"] # 0x210F (Planck constant over two pi) + - "ℓ": [t: "TE⠇"] # 0x2113 (Script small l (differs from 1d4c1: 4-56-123)) + - "℘": [tc: "TCL⠏"] # 0x2118 (Script Capital P) +# - "ℝ": [t: ""] # 0x211D (Double-struck capital R (no nemeth double struck)) +# - "ⅆ": [t: ""] # 0x2146 (Double-struck italic small d) +# - "ⅇ": [t: ""] # 0x2147 (Double-struck italic small e) +# - "⇚": [t: "⠀⠫⠪⠸⠸⠀"] # 0x21DA (Leftwards triple arrow) +# - "⇛": [t: "⠀⠫⠸⠸⠕⠀"] # 0x21DB (Rightwards triple arrow) + - "∔": [t: "⠐⠬⠣⠡⠻"] # 0x2214 (Dot plus) + - "√": [t: "⠜"] # 0x221A (Square root) + - "∛": [t: "⠣⠒⠜"] # 0x221B (Cube root) + - "∜": [t: "⠣⠲⠜"] # 0x221C (Fourth root) + - "∡": [t: "⠫⠪⠸⠫⠫⠁⠻"] # 0x2221 (Measured angle) + - "∹": [t: "⠤⠐⠂"] # 0x2239 (Excess) + - "∺": [t: "⠐⠤⠩⠡⠡⠣⠡⠡⠻"] # 0x223A (Geometric proportion) + - "∻": [t: "⠐⠈⠱⠩⠡⠣⠡⠻"] # 0x223B (Homothetic) + - "∽": [t: "⠠⠱"] # 0x223D (Reversed tilde) + - "≋": [t: "⠈⠱⠈⠱⠈⠱"] # 0x224B (Triple tilde) + - "≌": [t: "⠠⠱⠨⠅"] # 0x224C (All equal to) + - "≏": [t: "⠈⠣⠱"] # 0x224F (Difference between) + - "≔": [t: "⠐⠂⠨⠅"] # 0x2254 (Colon equals) + - "≕": [t: "⠨⠅⠐⠂"] # 0x2255 (Equals colon) + - "≖": [t: "⠀⠨⠡⠈⠨⠅⠻⠀"] # 0x2256 (Ring in equal to) + - "⊌": [t: "⠨⠬⠈⠫⠪⠒⠻"] # 0x228C (Multiset) + - "⊍": [t: "⠡⠈⠨⠬⠻"] # 0x228D (Multiset multiplication) + - "⊎": [t: "⠬⠈⠨⠬⠻"] # 0x228E (Multiset union) + - "⊕": [t: "⠫⠉⠸⠫⠬⠻"] # 0x2295 (Circled plus) + - "⊖": [t: "⠫⠉⠸⠫⠤⠻"] # 0x2296 (Circled minus) + - "⊗": [t: "⠫⠉⠸⠫⠈⠡⠻"] # 0x2297 (Circled times) + - "⊘": [t: "⠫⠉⠸⠫⠸⠌⠻"] # 0x2298 (Circled division slash) + - "⊙": [t: "⠫⠉⠸⠫⠡⠻"] # 0x2299 (Circled dot operator) + - "⊚": [t: "⠫⠉⠸⠫⠘⠨⠡⠻"] # 0x229a (Circled ring operator) + - "⊛": [t: "⠫⠉⠸⠫⠈⠼⠻"] # 0x229B (Circled asterisk operator) + - "⊜": [t: "⠫⠉⠸⠫⠨⠅⠻"] # 0x229C (Circled equals) + - "⊝": [t: "⠫⠉⠸⠫⠤⠤⠻"] # 0x229D (Circled dash) + - "⊞": [t: "⠫⠲⠸⠫⠬⠻"] # 0x229E (Squared plus) + - "⊟": [t: "⠫⠲⠸⠫⠤⠻"] # 0x229F (Squared minus) + - "⊠": [t: "⠫⠲⠸⠫⠈⠡⠻"] # 0x22A0 (Squared times) + - "⊡": [t: "⠫⠲⠸⠫⠡⠻"] # 0x22A1 (Squared dot operator) + - "⊢": [t: "⠫⠳⠒"] # 0x22A2 (Right tack) + - "⊣": [t: "⠫⠒⠳"] # 0x22A3 (Left tack) + - "⊤": [t: "⠫⠩⠳⠒"] # 0x22A4 (Down tack) +# - "⊥": [t: "⠀⠫⠏⠀ or ⠀⠫⠣⠳⠒⠀"] # 0x22A5 (Up tack (See also 27C2)) +# - "⊦": [t: ""] # 0x22A6 (Assertion) +# - "⊨": [t: ""] # 0x22A8 (TRUE) +# - "⊲": [t: ""] # 0x22B2 (Normal subgroup of) +# - "⊳": [t: ""] # 0x22B3 (Contains as normal subgroup) +# - "⊴": [t: ""] # 0x22B4 (Normal subgroup of or equal to) +# - "⊵": [t: ""] # 0x22B5 (Contains as normal subgroup or equal to) + - "⊶": [t: "⠫⠨⠡⠒⠡"] # 0x22B6 (Original of) + - "⊷": [t: "⠫⠡⠒⠨⠡"] # 0x22B7 (Image of) + - "⊸": [t: "⠫⠒⠨⠡"] # 0x22B8 (Multimap) +# - "⊾": [t: ""] # 0x22BE (Right angle w arc) + - "⊿": [t: "⠫⠞⠨⠗⠻"] # 0x22BF (Right triangle) + - "⋅": [t: "⠡"] # 0x22C5 (Dot operator) + - "∙": [t: "⠡"] # 0x2219 -- similar looking to 0x22c5, so using the same braille + - "⋆": [t: "⠫⠎"] # 0x22C6 (Star operator) + - "⋍": [t: "⠠⠱⠱"] # 0x22CD (Reversed tilde equals) + - "⋖": [t: "⠡⠈⠐⠅⠻"] # 0x22D6 (Less-than w dot) + - "⋗": [t: "⠡⠈⠨⠂⠻"] # 0x22D7 (Greater-than w dot) + - "⋘": [t: "⠐⠅⠈⠐⠅⠈⠐⠅⠻"] # 0x22D8 (Much less-than) + - "⋙": [t: "⠨⠂⠈⠨⠂⠈⠨⠂⠻"] # 0x22D9 (Much greater-than) + - "⋲": [t: "⠱⠈⠈⠑⠻"] # 0x22F2 (Element of w long horizontal stroke) + - "⋵": [t: "⠐⠈⠑⠣⠡⠻"] # 0x22F5 (Element of w dot above) + - "⋶": [t: "⠱⠈⠑"] # 0x22F6 (Element of w overbar) + - "⋺": [t: "⠱⠈⠈⠢⠻"] # 0x22FA (Contains w long horizontal stroke) + - "⋽": [t: "⠱⠈⠢"] # 0x22FD (Contains w overbar) + - "⌈": [t: "⠈⠘⠷"] # 0x2308 (Left ceiling) + - "⌉": [t: "⠈⠘⠾"] # 0x2309 (Right ceiling) + - "⌊": [t: "⠈⠰⠷"] # 0x230A (Left floor) + - "⌋": [t: "⠈⠰⠾"] # 0x230B (Right floor) + - "⌢": [t: "⠫⠁"] # 0x2322 (Frown) + - "⌣": [t: "⠫⠄"] # 0x2323 (Smile) + - "■": [t: "⠫⠸⠲"] # 0x25A0 (Filled square) + - "▫": [t: "⠫⠲"] # 0x25AB (Small white Square) + - "□": [t: "⠫⠲"] # 0x25A1 (Square) + - "◻": [t: "⠫⠲"] # 0x25FB (Square) + - "▬": [t: "⠫⠸⠗"] # 0x25AC (Filled rectangle) + - "▭": [t: "⠫⠗"] # 0x25AD (Rectangle) + - "▲": [t: "⠫⠸⠞"] # 0x25B2 (Black up-pointing triangle) + - "△": [t: "⠫⠞"] # 0x25B3 (White up-pointing triangle) + - "▼": [t: "⠸⠨⠫"] # 0x25BC (Black down-pointing triangle) + - "○": [t: "⠫⠉"] # 0x25CB (White circle) + - "●": [t: "⠫⠸⠉"] # 0x25CF (Black circle) + - "◫": [t: "⠫⠲⠸⠫⠳⠻"] # 0x25EB (White square w vertical bisecting line) +# - "⊥": [t: "⠫⠏"] # 0x27C2 (Perpendicular) + - "⟃": [t: "⠨⠡⠈⠸⠐⠅⠻"] # 0x27C3 (Open subset) + - "⟄": [t: "⠨⠡⠈⠸⠨⠂⠻"] # 0x27C4 (Open superset) + - "⟜": [t: "⠫⠨⠡⠒⠒"] # 0x27DC (Left multimap) + - "⟝": [t: "⠫⠳⠒⠒"] # 0x27DD (Long right tack) + - "⟞": [t: "⠫⠒⠒⠳"] # 0x27DE (Long left tack) + - "⟟": [t: "⠫⠣⠳⠒⠒⠨⠡"] # 0x27DF (Up tack w circle above) + - "⟦": [t: "⠈⠸⠷"] # 0x27E6 (Mathematical left white square bracket) + - "⟧": [t: "⠈⠸⠾"] # 0x27E7 (Mathematical right white square bracket) + - "⟨": [t: "⠨⠨⠷"] # 0x27E8 (Mathematical left angle bracket) + - "⟩": [t: "⠨⠨⠾"] # 0x27E9 (Mathematical right angle bracket) + - "⟵": [t: "⠀⠫⠪⠒⠒⠒⠀"] # 0x27F5 (Long leftwards arrow ) + - "⟶": [t: "⠀⠫⠒⠒⠒⠕⠀"] # 0x27F6 (Long rightwards arrow) + - "⟷": [t: "⠀⠫⠪⠒⠒⠒⠕⠀"] # 0x27F7 (Long left right arrow) + - "⟸": [t: "⠀⠫⠪⠶⠶⠶⠀"] # 0x27F8 (Long leftwards arrow ) + - "⟹": [t: "⠀⠫⠶⠶⠶⠕⠀"] # 0x27F9 (Long rightwards arrow) + - "⟺": [t: "⠀⠫⠪⠶⠶⠶⠕⠀"] # 0x27FA (Long left right arrow) + - "⟻": [t: "⠀⠫⠪⠒⠒⠒⠳⠀"] # 0x27FB (Long leftwards arrow from bar) + - "⟼": [t: "⠀⠫⠳⠒⠒⠒⠕⠀"] # 0x27FC (Long rightwards arrow from bar) + - "⟽": [t: "⠀⠫⠪⠶⠶⠶⠳⠀"] # 0x27FD (Long leftwards double arrow from bar) + - "⟾": [t: "⠀⠫⠳⠶⠶⠶⠕⠀"] # 0x27FE (Long rightwards double arrow from bar) + - "⟿": [t: "⠀⠫⠢⠤⠔⠒⠢⠤⠔⠒⠢⠕⠀"] # 0x27FF (Long rightwards squiggle arrow) + - "⤡": [t: "⠀⠫⠘⠪⠒⠒⠕⠀"] # 0x2921 (North west and south east arrow) + - "⤢": [t: "⠀⠫⠰⠪⠒⠒⠕⠀"] # 0x2922 (North east and south west arrow) + - "🣐": # 0x1f8d0 (Long rightwards arrow over leftwards arrow (equilibrium)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠠⠪⠒⠒"] + - "🣑": # 0x1f8d1 (Long rightwards harpoon over leftwards harpoon (equilibrium)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠠⠪⠒⠒"] + - "🣒": # 0x1f8d2 (Long rightwards harpoon over short leftwards harpoon (equilibrium)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] + - "🣓": # 0x1f8d3 (Short rightwards harpoon over long leftwards harpoon (equilibrium, trend to the right)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒⠒"] + else: [t: "⠫⠒⠈⠕⠫⠠⠪⠒⠒"] + - "🣔": # 0x1f8d4 (Long leftwards harpoon over short rightwards harpoon (equilibrium, trend to the left)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠪⠒⠒⠫⠒⠕"] + else: [t: "⠫⠈⠪⠒⠒⠫⠒⠠⠕"] + - "🣕": # 0x1f8d5 (Short leftwards harpoon over long rightwards harpoon (equilibrium, trend to the left)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠪⠒⠫⠒⠒⠕"] + else: [t: "⠫⠈⠪⠒⠫⠒⠒⠠⠕"] + - "⥂": # 0x2942 (Long rightwards harpoon over short leftwards harpoon (equilibrium)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] + - "⥄": # 0x2944 (Short rightwards harpoon over long leftwards harpoon (equilibrium, trend to the right)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒⠒"] + else: [t: "⠫⠒⠈⠕⠫⠠⠪⠒⠒"] + - "⥃": # 0x2943 (Long leftwards harpoon over short rightwards harpoon (equilibrium, trend to the left)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠪⠒⠒⠫⠒⠕"] + else: [t: "⠫⠈⠪⠒⠒⠫⠒⠠⠕"] + - "🣖": [t: "⠌⠫⠒⠒⠕"] # 0x1f8d6 (long rightwards arrow with through X) + - "🣗": [t: "⠌⠫⠒⠒⠕"] # 0x1f8d7 (long rightwards arrow with double slash) chem code says it is same as 0x1f8d6 + + + - "⦃": [t: "⠨⠸⠷"] # 0x2983 (Left white curly bracket) + - "⦄": [t: "⠨⠸⠾"] # 0x2984 (Right white curly bracket) + - "⦑": [t: "⠡⠈⠨⠨⠷⠻"] # 0x2991 (Left angle bracket w dot) + - "⦒": [t: "⠡⠈⠨⠨⠾⠻"] # 0x2992 (Right angle bracket w dot) + - "⧄": [t: "⠫⠲⠸⠫⠔⠻"] # 0x29C4 (Square rising diagonal slash) + - "⧅": [t: "⠫⠲⠸⠫⠢⠻"] # 0x29C5 (Square falling diagonal slash) + - "⧆": [t: "⠫⠲⠸⠫⠈⠼⠻"] # 0x29C6 (Squared asterisk) + - "⧊": [t: "⠐⠫⠞⠣⠡⠻"] # 0x29CA (Triangle w dot above) + - "⧋": [t: "⠫⠞⠱"] # 0x29CB (Triangle w underbar) + - "⧌": [t: "⠫⠞⠎"] # 0x29CC (s in triangle §114) + - "⧦": [t: "⠫⠳⠶⠶⠳"] # 0x29E6 (Gleich stark) + - "⨌": [t: "⠮⠮⠮⠮"] # 0x2A0C (Quadruple integral operator) + - "⨍": [t: "⠮⠈⠱⠻"] # 0x2A0D (Finite part integral) + - "⨎": [t: "⠮⠈⠱⠱⠻"] # 0x2A0E (Integral w double stroke) + - "⨖": [t: "⠮⠈⠫⠲⠻"] # 0x2A16 (Quaternion integral operator) + - "⨘": [t: "⠮⠈⠈⠡⠻"] # 0x2A18 (Integral w times sign) + - "⨙": [t: "⠮⠈⠨⠩⠻"] # 0x2A19 (Integral w intersection) + - "⨚": [t: "⠮⠈⠨⠬⠻"] # 0x2A1A (Integral w union) + - "⨛": [t: "⠣⠮"] # 0x2A1B (Integral w overbar (upper)) + - "⨜": [t: "⠩⠮"] # 0x2A1C (Integral w underbar (lower)) + - "⨢": [t: "⠐⠬⠣⠨⠡⠻"] # 0x2A22 (Plus sign w small circle above) + - "⨣": [t: "⠐⠬⠣⠸⠣⠻"] # 0x2A23 (Plus sign w circumflex accent above) + - "⨤": [t: "⠐⠬⠣⠈⠱⠻"] # 0x2A24 (Plus sign w tilde above) + - "⨥": [t: "⠐⠬⠩⠡⠻"] # 0x2A25 (Plus sign w dot below) + - "⨪": [t: "⠐⠱⠩⠡⠻"] # 0x2A2A (Minus sign w dot below) + - "⨰": [t: "⠐⠈⠡⠣⠡⠻"] # 0x2A30 (Multiplication sign w dot above) + - "⨱": [t: "⠈⠡⠱"] # 0x2A31 (Multiplication sign w underbar) + - "⨸": [t: "⠫⠉⠸⠫⠨⠌⠻"] # 0x2A38 (Circled division sign) + - "⨹": [t: "⠫⠞⠸⠫⠬⠻"] # 0x2A39 (Plus sign in triangle) + - "⨺": [t: "⠫⠞⠸⠫⠤⠻"] # 0x2A3A (Minus sign in triangle) + - "⨻": [t: "⠫⠞⠸⠫⠈⠡⠻"] # 0x2A3B (Multiplication sign in triangle) + - "⩀": [t: "⠡⠈⠨⠩⠻"] # 0x2A40 (Intersection w dot) + - "⩁": [t: "⠤⠈⠨⠬⠻"] # 0x2A41 (Union w minus sign) + - "⩂": [t: "⠱⠨⠬"] # 0x2A42 (Union w overbar) + - "⩃": [t: "⠱⠨⠩"] # 0x2A43 (Intersection w overbar) + - "⩑": [t: "⠐⠈⠩⠣⠡⠻"] # 0x2A51 (Logical AND w dot above) + - "⩒": [t: "⠐⠈⠬⠣⠡⠻"] # 0x2A52 (Logical OR w dot above) + - "⩜": [t: "⠱⠈⠈⠩⠻"] # 0x2A5C (Logical AND with horizontal dash) + - "⩝": [t: "⠱⠈⠈⠬⠻"] # 0x2A5D (Logical OR with horizontal dash) + - "⩞": [t: "⠱⠱⠈⠩"] # 0x2A5E (Logical AND w double overbar) + - "⩟": [t: "⠈⠩⠱"] # 0x2A5F (Logical AND w underbar) + - "⩠": [t: "⠈⠩⠱⠱"] # 0x2A60 (Logical AND w double underbar) + - "⩢": [t: "⠱⠱⠈⠬"] # 0x2A62 (Logical OR w double overbar) + - "⩣": [t: "⠈⠬⠱⠱"] # 0x2A63 (Logical OR w double underbar) + - "⩦": [t: "⠐⠨⠅⠩⠡⠻"] # 0x2A66 (Equals sign w dot below) + - "⩧": [t: "⠐⠸⠇⠣⠡⠻"] # 0x2A67 (Identical w dot above) + - "⩪": [t: "⠐⠈⠱⠣⠡⠻"] # 0x2A6A (Tilde operator w dot above) + - "⩬": [t: "⠈⠱⠱⠈⠱"] # 0x2A6C (Similar minus similar) + - "⩭": [t: "⠐⠈⠱⠨⠅⠣⠡⠻"] # 0x2A6D (Congruent w dot above) + - "⩮": [t: "⠐⠨⠅⠣⠈⠼⠻"] # 0x2A6E (Equals w asterisk) + - "⩯": [t: "⠐⠈⠱⠈⠱⠣⠸⠣⠻"] # 0x2A6F (Almost equal to w circumflex) + - "⩰": [t: "⠈⠱⠈⠱⠨⠅"] # 0x2A70 (Approximately equal or equal to) + - "⩱": [t: "⠨⠅⠬"] # 0x2A71 (Equals sign above plus sign) + - "⩲": [t: "⠬⠨⠅"] # 0x2A72 (Plus sign above equals sign) + - "⩳": [t: "⠨⠅⠈⠱"] # 0x2A73 (Equals sign above tilde operator) + - "⩴": [t: "⠐⠂⠐⠂⠨⠅"] # 0x2A74 (Double colon equal) + - "⩵": [t: "⠨⠅⠨⠅"] # 0x2A75 (Two consecutive equals signs) + - "⩶": [t: "⠨⠅⠨⠅⠨⠅"] # 0x2A76 (Three consecutive equals signs) + - "⩷": [t: "⠐⠨⠅⠩⠡⠡⠣⠡⠡⠻"] # 0x2A77 (Equals sign w two dots above and two dots below) + - "⩸": [t: "⠐⠸⠇⠣⠡⠡⠡⠡⠻"] # 0x2A78 (Equivalent w four dots above) + - "⩹": [t: "⠐⠅⠈⠨⠡⠻"] # 0x2A79 (Less than w circle inside) + - "⩺": [t: "⠨⠂⠈⠨⠡⠻"] # 0x2A7A (Greater than w circle inside) + - "⩻": [t: "⠐⠐⠅⠣⠸⠦⠻"] # 0x2A7B (Less-than w question mark above) + - "⩼": [t: "⠐⠨⠂⠣⠸⠦⠻"] # 0x2A7C (Greater-than w question mark above) + - "⪅": [t: "⠐⠅⠈⠱⠈⠱"] # 0x2A85 (Less-than or approximate) + - "⪆": [t: "⠨⠂⠈⠱⠈⠱"] # 0x2A86 (Greater-than or approximate) + - "⪋": [t: "⠐⠅⠨⠅⠨⠂"] # 0x2A8B (Less-than above double-line equal above greater-than) + - "⪌": [t: "⠨⠂⠨⠅⠐⠅"] # 0x2A8C (Greater-than above double-line equal above less-than) + - "⪍": [t: "⠐⠅⠈⠱⠱"] # 0x2A8D (Less-than above similar or equal to) + - "⪎": [t: "⠨⠂⠈⠱⠱"] # 0x2A8E (Greater-than above similar or equal to) + - "⪏": [t: "⠐⠅⠈⠱⠨⠂"] # 0x2A8F (Less-than above similar above greater-than) + - "⪐": [t: "⠨⠂⠈⠱⠐⠅"] # 0x2A90 (Greater-than above similar above less-than) + - "⪑": [t: "⠐⠅⠨⠂⠨⠅"] # 0x2A91 (Less-than above greater-than above double-line equal) + - "⪒": [t: "⠨⠂⠐⠅⠨⠅"] # 0x2A92 (Greater-than above less-than above double-line equal) + - "⪙": [t: "⠨⠅⠐⠅"] # 0x2A99 (Double-line equal to or less-than) + - "⪚": [t: "⠨⠅⠨⠂"] # 0x2A9A (Double-line equal to or greater-than) + - "⪝": [t: "⠈⠱⠐⠅"] # 0x2A9D (Similar or less-than) + - "⪞": [t: "⠈⠱⠨⠂"] # 0x2A9E (Similar or greater-than) + - "⪟": [t: "⠈⠱⠐⠅⠨⠅"] # 0x2A9F (Similar above less-than above equals sign) + - "⪠": [t: "⠈⠱⠨⠂⠨⠅"] # 0x2AA0 (Similar above greater-than above equals sign) + - "⪮": [t: "⠈⠣⠨⠅"] # 0x2AAE (Equals sign with bumpy above) + - "⪽": [t: "⠡⠈⠸⠐⠅⠻"] # 0x2ABD (Subset w dot) + - "⪾": [t: "⠡⠈⠸⠨⠂⠻"] # 0x2ABE (Superset w dot) + - "⪿": [t: "⠐⠸⠐⠅⠩⠬⠻"] # 0x2ABF (Subset with plus sign below) + - "⫀": [t: "⠐⠸⠨⠂⠩⠬⠻"] # 0x2AC0 (Superset with plus sign below) + - "⫁": [t: "⠐⠸⠐⠅⠩⠈⠡⠻"] # 0x2AC1 (Subset with multiplication sign below) + - "⫂": [t: "⠐⠸⠨⠂⠩⠈⠡⠻"] # 0x2AC2 (Superset with multiplication sign below) + - "⫅": [t: "⠸⠐⠅⠨⠅"] # 0x2AC5 (Subset of above equals sign) + - "⫆": [t: "⠸⠨⠂⠨⠅"] # 0x2AC6 (Superset of above equals sign) + - "⫇": [t: "⠸⠐⠅⠈⠱"] # 0x2AC7 (Subset of above tilde operator) + - "⫈": [t: "⠸⠨⠂⠈⠱"] # 0x2AC8 (Superset of above tilde operator) + - "⫉": [t: "⠸⠐⠅⠈⠱⠈⠱"] # 0x2AC9 (Subset of above almost equal to) + - "⫊": [t: "⠸⠨⠂⠈⠱⠈⠱"] # 0x2ACA (Superset of above almost equal to) + - "⫋": [t: "⠸⠐⠅⠌⠨⠅"] # 0x2ACB (Subset of above not equals sign) + - "⫌": [t: "⠸⠨⠂⠌⠨⠅"] # 0x2ACC (Superset of above not equals sign) + - "⫓": [t: "⠸⠐⠅⠸⠨⠂"] # 0x2AD3 (Subset above superset) + - "⫔": [t: "⠸⠨⠂⠸⠐⠅"] # 0x2AD4 (Superset above subset) + - "⫕": [t: "⠸⠐⠅⠸⠐⠅"] # 0x2AD5 (Subset above subset) + - "⫖": [t: "⠸⠨⠂⠸⠨⠂"] # 0x2AD6 (Superset above superset) + - "⫗": [t: "⠸⠨⠂⠐⠸⠐⠅"] # 0x2AD7 (Superset beside subset) + - "⫯": [t: "⠫⠩⠨⠡⠒⠒"] # 0x2AEF (Vertical line w circle above) + - "⫰": [t: "⠫⠣⠨⠡⠒⠒"] # 0x2AF0 (Vertical line w circle below) + - "⫱": [t: "⠫⠣⠨⠡⠒⠒⠳"] # 0x2AF1 (Down tack w circle below) + - "⫲": [t: "⠱⠈⠫⠇⠻"] # 0x2AF2 (Parallel w horizontal stroke) + - "⫳": [t: "⠈⠱⠈⠫⠇⠻"] # 0x2AF3 (Parallel w tilde operator) + - "⫴": [t: "⠳⠳⠳"] # 0x2AF4 (Triple vertical bar binary relation) + - "⬬": [t: "⠫⠸⠑"] # 0x2B2C (Black ellipse) + - "⬭": [t: "⠫⠑"] # 0x2B2D (White ellipse) + - "⬰": [t: "⠨⠡⠈⠫⠪⠒⠒⠻"] # 0x2B30 (Left arrow w small circle) + - "⬱": [t: "⠫⠪⠒⠒⠫⠪⠒⠒⠫⠪⠒⠒"] # 0x2B31 (Three leftwards arrows) + - "⬲": [t: "⠫⠪⠒⠒⠈⠫⠉⠸⠫⠬⠻"] # 0x2B32 (Left arrow w circled plus) + - "⮖": [t: "⠨⠡"] # 0x2B96 (Standard State Symbol) + - "⯃": [t: "⠫⠸⠦"] # 0x2BC3 (Horizontal black octagon) + + # SRE lists this as a custom transcription + - "⋊": [t: "⠈⠡⠳"] # 0x22ca (RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT) diff --git a/Rules/Braille/Nemeth/unicode.yaml b/Rules/Braille/Nemeth/unicode.yaml index 83c850fe8..5b9c973f9 100644 --- a/Rules/Braille/Nemeth/unicode.yaml +++ b/Rules/Braille/Nemeth/unicode.yaml @@ -20,32 +20,10 @@ - "7": [t: "N⠶"] # 0x37 - "8": [t: "N⠦"] # 0x38 - "9": [t: "N⠔"] # 0x39 - - "A": [t: "CL⠁"] # 0x41 - - "B": [t: "CL⠃"] # 0x42 - - "C": [t: "CL⠉"] # 0x43 - - "D": [t: "CL⠙"] # 0x44 - - "E": [t: "CL⠑"] # 0x45 - - "F": [t: "CL⠋"] # 0x46 - - "G": [t: "CL⠛"] # 0x47 - - "H": [t: "CL⠓"] # 0x48 - - "I": [t: "CL⠊"] # 0x49 - - "J": [t: "CL⠚"] # 0x4a - - "K": [t: "CL⠅"] # 0x4b - - "L": [t: "CL⠇"] # 0x4c - - "M": [t: "CL⠍"] # 0x4d - - "N": [t: "CL⠝"] # 0x4e - - "O": [t: "CL⠕"] # 0x4f - - "P": [t: "CL⠏"] # 0x50 - - "Q": [t: "CL⠟"] # 0x51 - - "R": [t: "CL⠗"] # 0x52 - - "S": [t: "CL⠎"] # 0x53 - - "T": [t: "CL⠞"] # 0x54 - - "U": [t: "CL⠥"] # 0x55 - - "V": [t: "CL⠧"] # 0x56 - - "W": [t: "CL⠺"] # 0x57 - - "X": [t: "CL⠭"] # 0x58 - - "Y": [t: "CL⠽"] # 0x59 - - "Z": [t: "CL⠵"] # 0x5a + # Latin capitals: C + cells from lowercase a-z via spell/translate. + - "A-Z": # 0x41 - 0x5a + - tc: "C" + - spell: "translate('.', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')" - "a": [t: "L⠁"] # 0x61 - "b": [t: "L⠃"] # 0x62 - "c": [t: "L⠉"] # 0x63 diff --git a/Rules/Braille/Russian/unicode-full.yaml b/Rules/Braille/Russian/unicode-full.yaml index 813669ecc..b562dc302 100644 --- a/Rules/Braille/Russian/unicode-full.yaml +++ b/Rules/Braille/Russian/unicode-full.yaml @@ -57,7 +57,7 @@ - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 - - tc: "B" + - tc: "BC" - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒𝛓𝛔𝛕𝛖𝛗𝛘𝛙𝛚', 'αβγδεζηθικλμνξοπρςστυφχψω')" @@ -123,23 +123,23 @@ - spell: "translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz')" - "𝐴-𝑍": # 0x1d434 - 0x1d44d - - tc: "IC" + - tc: "C" - spell: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'abcdefghijklmnopqrstuvwxyz')" - "𝑎-𝑧": # 0x1d44e - 0x1d467 - - tc: "I" + - tc: "" - spell: "translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔𝑕𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz')" - "𝑨-𝒁": # 0x1d468 - 0x1d481 - - tc: "BIC" + - tc: "BC" - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'abcdefghijklmnopqrstuvwxyz')" - "𝒂-𝒛": # 0x1d482 - 0x1d49b - - tc: "BI" + - tc: "B" - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 - - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'abcdefghijklmnopqrstuvwxyz')" - "𝖺-𝗓": # 0x1d5ba - 0x1d5d3 - spell: "translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz')" @@ -148,7 +148,7 @@ - test: if: "not($IgnoreBold)" then: [tc: "B"] - - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'abcdefghijklmnopqrstuvwxyz')" - "𝗮-𝘇": # 0x1d5ee - 0x1d607 - test: @@ -157,27 +157,27 @@ - spell: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" - "𝘈-𝘡": # 0x1d608 - 0x1d621 - - tc: "IC" - - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - tc: "C" + - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'abcdefghijklmnopqrstuvwxyz')" - "𝘢-𝘻": # 0x1d622 - 0x1d63b - - tc: "I" + - tc: "" - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" - "𝘼-𝙕": # 0x1d63c - 0x1d655 - # - tc: "bold italic" + # - tc: "bold italicC" - test: if: "$IgnoreBold" - then: [tc: I] - else: [tc: "BI"] - - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + then: [tc: "C"] + else: [tc: "B"] + - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'abcdefghijklmnopqrstuvwxyz')" - "𝙖-𝙯": # 0x1d656 - 0x1d66f # - tc: "bold italic" - test: if: "$IgnoreBold" - then: [tc: I] - else: [tc: "BI"] + then: [tc: ""] + else: [tc: "B"] - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" - "𝙰-𝚉": # 0x1d670 - 0x1d689 @@ -194,7 +194,7 @@ - test: if: "not($IgnoreBold)" then: [tc: "B"] - - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝛂-𝛚": # 0x1d6c2 - 0x1d6da - test: @@ -215,7 +215,7 @@ - spell: "translate('.', '𝛛𝛜𝛝𝛞𝛟𝛠𝛡', '∂εθκφρπ')" - "𝛢-𝛺": # 0x1d6e2 - 0x1d6fa - - tc: "IC" + - tc: "C" - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹𝛺', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝛼-𝜔": # 0x1d6fc - 0x1d714 @@ -230,20 +230,20 @@ - spell: "translate('.', '𝜕𝜖𝜗𝜘𝜙𝜚𝜛', '∂εθκφρπ')" - "𝜜-𝜴": # 0x1d71c - 0x1d734 - # - tc: "bold italic" - - tc: 'BIC⠁' + # - tc: "bold italicC" + - tc: 'BC⠁' - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳𝜴', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝜶-𝝎": # 0x1d736 - 0x1d74e - - tc: "BI" + - tc: "B" - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆𝝇𝝈𝝉𝝊𝝋𝝌𝝍𝝎', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝝏𝝐𝝑𝝒𝝓𝝔𝝕": # 0x1d74f - 0x1d755 - - tc: "BI" + - tc: "B" - spell: "translate('.', '𝝏𝝐𝝑𝝒𝝓𝝔𝝕', '∂εκθφρπ')" - "𝜵": # 0x1d735 - - tc: "BI" + - tc: "B" - spell: "translate('.', '𝜵', '∇')" - "𝝖-𝝮": # 0x1d756 - 0x1d76e @@ -263,19 +263,19 @@ - spell: "translate('.', '𝜵', '∇')" - "𝞐-𝞨": # 0x1d790 - 0x1d7a8 - # - tc: "bold italic" + # - tc: "bold italicC" - test: if: "$IgnoreBold" - then: [tc: I] - else: [tc: "BI"] - - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + then: [tc: "C"] + else: [tc: "B"] + - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝞪-𝟂": # 0x1d7aa - 0x1d7c2 # - tc: "bold italic" - test: if: "$IgnoreBold" - then: [tc: I] - else: [tc: "BI"] + then: [tc: ""] + else: [tc: "B"] - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺𝞻𝞼𝞽𝞾𝞿𝟀𝟁𝟂', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝟃𝟄𝟅𝟆𝟇𝟈𝟉": # 0x1d7c3 - 0x1d7c9 diff --git a/Rules/Braille/Swedish/unicode-full.yaml b/Rules/Braille/Swedish/unicode-full.yaml index 4faab15c3..51400827b 100644 --- a/Rules/Braille/Swedish/unicode-full.yaml +++ b/Rules/Braille/Swedish/unicode-full.yaml @@ -1,50 +1,50 @@ --- - 𝐀-𝐙: # 0x1d400 - 0x1d419 - - tc: B - - spell: translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - tc: BC + - spell: translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'abcdefghijklmnopqrstuvwxyz') - 𝐚-𝐳: # 0x1d41a - 0x1d433 - tc: B - spell: translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz') - 𝐴-𝑍: # 0x1d434 - 0x1d44d - - tc: I - - spell: translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - tc: "C" + - spell: translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'abcdefghijklmnopqrstuvwxyz') - 𝑎-𝑧: # 0x1d44e - 0x1d467 - - tc: I + - tc: "" - spell: translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔𝑕𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz') - 𝑨-𝒁: # 0x1d468 - 0x1d481 - - tc: BI - - spell: translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - tc: BC + - spell: translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'abcdefghijklmnopqrstuvwxyz') - 𝒂-𝒛: # 0x1d482 - 0x1d49b - - tc: BI + - tc: B - spell: translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz') - 𝒜-𝒵: # 0x1d49c - 0x1d4b5 - - tc: Ts - - spell: translate('.', '𝒜𝒝𝒞𝒟𝒠𝒡𝒢𝒣𝒤𝒥𝒦𝒧𝒨𝒩𝒪𝒫𝒬𝒭𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - tc: TsC + - spell: translate('.', '𝒜𝒝𝒞𝒟𝒠𝒡𝒢𝒣𝒤𝒥𝒦𝒧𝒨𝒩𝒪𝒫𝒬𝒭𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'abcdefghijklmnopqrstuvwxyz') - 𝒶-𝓏: # 0x1d4b6 - 0x1d4cf - tc: Ts - spell: translate('.', '𝒶𝒷𝒸𝒹𝒺𝒻𝒼𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝓄𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏', 'abcdefghijklmnopqrstuvwxyz') - 𝓐-𝓩: # 0x1d4d0 - 0x1d4e9 - - tc: BTs - - spell: translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - tc: BTsC + - spell: translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'abcdefghijklmnopqrstuvwxyz') - 𝓪-𝔃: # 0x1d4ea - 0x1d503 - tc: BTs - spell: translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz') - 𝔄-𝔝: # 0x1d504 - 0x1d51d - tc: DC - - spell: translate('.', '𝔄𝔅𝔆𝔇𝔈𝔉𝔊𝔋𝔌𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔕𝔖𝔗𝔘𝔙𝔚𝔛𝔜𝔝', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - spell: translate('.', '𝔄𝔅𝔆𝔇𝔈𝔉𝔊𝔋𝔌𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔕𝔖𝔗𝔘𝔙𝔚𝔛𝔜𝔝', 'abcdefghijklmnopqrstuvwxyz') - 𝔞-𝔷: # 0x1d51e - 0x1d537 - tc: D - spell: translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz') - 𝔸-𝔹: # 0x1d538 - 0x1d539 - - tc: "𝔹" - - spell: translate('.', '𝔸𝔹', 'AB') + - tc: "𝔹C" + - spell: translate('.', '𝔸𝔹', 'ab') - 𝔻-𝔾: # 0x1d53b - 0x1d53e - - tc: "𝔹" - - spell: translate('.', '𝔻𝔼𝔽𝔾', 'DEFG') + - tc: "𝔹C" + - spell: translate('.', '𝔻𝔼𝔽𝔾', 'defg') - 𝕀-𝕄: # 0x1d540 - 0x1d544 - - tc: "𝔹" - - spell: translate('.', '𝕀𝕁𝕂𝕃𝕄', 'IJKLM') + - tc: "𝔹C" + - spell: translate('.', '𝕀𝕁𝕂𝕃𝕄', 'ijklm') - "ℂ": [t: "𝔹CL⠉"] # 0x2102 - "ℍ": [t: "𝔹CL⠓"] # 0x210d @@ -52,77 +52,77 @@ - "𝕆": [t: "𝔹CL⠕"] # 0x1d546 - 𝕊-𝕐: # 0x1d54a - 0x1d550 - - tc: "𝔹" - - spell: translate('.', '𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'STUVWXY') + - tc: "𝔹C" + - spell: translate('.', '𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'stuvwxy') - 𝕒-𝕫: # 0x1d552 - 0x1d56b - tc: '' - spell: translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz') - 𝕬-𝖅: # 0x1d56c - 0x1d585 - tc: BDC - - spell: translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - spell: translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'abcdefghijklmnopqrstuvwxyz') - 𝖆-𝖟: # 0x1d586 - 0x1d59f - tc: BD - spell: translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz') - 𝖠-𝖹: # 0x1d5a0 - 0x1d5b9 - tc: SC - - spell: translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - spell: translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'abcdefghijklmnopqrstuvwxyz') - 𝖺-𝗓: # 0x1d5ba - 0x1d5d3 - tc: S - spell: translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz') - 𝗔-𝗭: # 0x1d5d4 - 0x1d5ed - tc: BSC - - spell: translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - spell: translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'abcdefghijklmnopqrstuvwxyz') - 𝗮-𝘇: # 0x1d5ee - 0x1d607 - tc: BS - spell: translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz') - 𝘈-𝘡: # 0x1d608 - 0x1d621 - - tc: ISC - - spell: translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - tc: SC + - spell: translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'abcdefghijklmnopqrstuvwxyz') - 𝘢-𝘻: # 0x1d622 - 0x1d63b - - tc: IS + - tc: S - spell: translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz') - 𝘼-𝙕: # 0x1d63c - 0x1d655 - - tc: BISC - - spell: translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - tc: BSC + - spell: translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'abcdefghijklmnopqrstuvwxyz') - 𝙖-𝙯: # 0x1d656 - 0x1d66f - - tc: BIS + - tc: BS - spell: translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz') - 𝙰-𝚉: # 0x1d670 - 0x1d689 - tc: C - - spell: translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - spell: translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'abcdefghijklmnopqrstuvwxyz') - 𝚊-𝚥: # 0x1d68a - 0x1d6a5 - tc: '' - spell: translate('.', '𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣𝚤𝚥', 'abcdefghijklmnopqrstuvwxyz') - 𝚨-𝚿: # 0x1d6a8 - 0x1d6bf - tc: BCG - - spell: translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ') + - spell: translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝛂-𝛙: # 0x1d6c2 - 0x1d6d9 - tc: BG - spell: translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒𝛓𝛔𝛕𝛖𝛗𝛘𝛙', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝛢-𝛹: # 0x1d6e2 - 0x1d6f9 - - tc: ICG - - spell: translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ') + - tc: CG + - spell: translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝛼-𝜓: # 0x1d6fc - 0x1d713 - - tc: IG + - tc: G - spell: translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌𝜍𝜎𝜏𝜐𝜑𝜒𝜓', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝜜-𝜳: # 0x1d71c - 0x1d733 - - tc: BICG - - spell: translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ') + - tc: BCG + - spell: translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝜶-𝝍: # 0x1d736 - 0x1d74d - - tc: BIG + - tc: BG - spell: translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆𝝇𝝈𝝉𝝊𝝋𝝌𝝍', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝝖-𝝭: # 0x1d756 - 0x1d76d - tc: BSCG - - spell: translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ') + - spell: translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝝰-𝞇: # 0x1d770 - 0x1d787 - tc: BSG - spell: translate('.', '𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿𝞀𝞁𝞂𝞃𝞄𝞅𝞆𝞇', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝞐-𝞧: # 0x1d790 - 0x1d7a7 - - tc: BISCG - - spell: translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ') + - tc: BSCG + - spell: translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝞪-𝟁: # 0x1d7aa - 0x1d7c1 - - tc: BISG + - tc: BSG - spell: translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺𝞻𝞼𝞽𝞾𝞿𝟀𝟁', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝟎-𝟗: # 0x1d7ce - 0x1d7d7 - tc: BN diff --git a/Rules/Braille/Swedish/unicode.yaml b/Rules/Braille/Swedish/unicode.yaml index 2e69ae106..6d4e84c4e 100644 --- a/Rules/Braille/Swedish/unicode.yaml +++ b/Rules/Braille/Swedish/unicode.yaml @@ -427,32 +427,10 @@ - "Ỹ": [t: "CL⠤𝑐L⠽"] - "Ỵ": [t: "CL⠠𝑐L⠽"] - - "A": [t: "CL⠁"] # 0x41 - - "B": [t: "CL⠃"] # 0x42 - - "C": [t: "CL⠉"] # 0x43 - - "D": [t: "CL⠙"] # 0x44 - - "E": [t: "CL⠑"] # 0x45 - - "F": [t: "CL⠋"] # 0x46 - - "G": [t: "CL⠛"] # 0x47 - - "H": [t: "CL⠓"] # 0x48 - - "I": [t: "CL⠊"] # 0x49 - - "J": [t: "CL⠚"] # 0x4a - - "K": [t: "CL⠅"] # 0x4b - - "L": [t: "CL⠇"] # 0x4c - - "M": [t: "CL⠍"] # 0x4d - - "N": [t: "CL⠝"] # 0x4e - - "O": [t: "CL⠕"] # 0x4f - - "P": [t: "CL⠏"] # 0x50 - - "Q": [t: "CL⠟"] # 0x51 - - "R": [t: "CL⠗"] # 0x52 - - "S": [t: "CL⠎"] # 0x53 - - "T": [t: "CL⠞"] # 0x54 - - "U": [t: "CL⠥"] # 0x55 - - "V": [t: "CL⠧"] # 0x56 - - "W": [t: "CL⠺"] # 0x57 - - "X": [t: "CL⠭"] # 0x58 - - "Y": [t: "CL⠽"] # 0x59 - - "Z": [t: "CL⠵"] # 0x5a + # Latin capitals: C + cells from lowercase a-z via spell/translate. + - "A-Z": # 0x41 - 0x5a + - tc: "C" + - spell: "translate('.', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')" - "α": [t: "GL⠁"] # 0x3b1 - "β": [t: "GL⠃"] # 0x3b2 diff --git a/Rules/Braille/UEB/unicode-full.yaml b/Rules/Braille/UEB/unicode-full.yaml index 9114b4de6..b57660aeb 100644 --- a/Rules/Braille/UEB/unicode-full.yaml +++ b/Rules/Braille/UEB/unicode-full.yaml @@ -1,17 +1,6 @@ --- - - " ": [t: "W"] # 0x2000 - - " ": [t: "W"] # 0x2001 - - " ": [t: "W"] # 0x2002 - - " ": [t: "W"] # 0x2003 - - " ": [t: "W"] # 0x2004 - - " ": [t: "W"] # 0x2005 - - " ": [t: "W"] # 0x2006 - - " ": [t: "W"] # 0x2007 - - " ": [t: "W"] # 0x2008 - - " ": [t: "W"] # 0x2009 - - " ": [t: "W"] # 0x200a - - "​": [t: "W"] # 0x200b + - " -​": [t: "W"] # 0x2000 - 0x200b (various spaces / ZWSP) - "
": [t: "W"] # 0x2028 - "
": [t: "W"] # 0x2029 - " ": [t: "W"] # 0x202f @@ -199,12 +188,12 @@ - "ℰ": [tc: "TCL⠑"] # 0x2130 (Script Capital E) - "ℱ": [tc: "TCL⠋"] # 0x2131 (Script Capital F) - "ℳ": [tc: "TCL⠍"] # 0x2133 (Script Capital M) - - "ℴ": [tc: "TL⠕"] # 0x21334 (Script Small O) + - "ℴ": [tc: "TL⠕"] # 0x2134 (Script Small O) - "ℌ": [tc: "DCL⠓"] # 0x210C (Fraktur Capital H) - "ℑ": [tc: "DCL⠊"] # 0x2111 (Fraktur Capital I) - "ℜ": [tc: "DCL⠗"] # 0x211C (Fraktur Capital R) - "ℨ": [tc: "DCL⠵"] # 0x2128 (Fraktur Capital Z) - - "ℭ": [tc: "DCL⠉"] # 0x22DC (Fraktur Capital C) + - "ℭ": [tc: "DCL⠉"] # 0x212D (Fraktur Capital C) - "ℂ": [tc: "𝔹CL⠉"] # 0x2102 - "ℍ": [tc: "𝔹CL⠓"] # 0x210d @@ -228,21 +217,21 @@ - spell: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'abcdefghijklmnopqrstuvwxyz')" - "𝖆-𝖟": # 0x1d586 - 0x1d59f - - tc: "D" + - tc: "BD" - 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) - - tc: "DC" + - "𝔸-𝕐": # 0x1D538 - 0x1D551 ('z' version is reserved) + - tc: "𝔹C" - spell: "translate('.', '𝔸𝔹𝔺𝔻𝔼𝔽𝔾𝔿𝕀𝕁𝕂𝕃𝕄𝕅𝕆𝕇𝕈𝕉𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'abcdefghijklmnopqrstuvwxyz')" - "𝕒-𝕫": # 0x1d552 - 0x1d56b - - tc: "D" + - tc: "𝔹" - spell: "translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz')" - "𝟘-𝟡": # 0x1d7d8 - 0x1d7e1 - - tc: "D" + - tc: "𝔹" - spell: "translate('.', '𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡', '0123456789')" # script chars in math alphabetic block and also MathType private use area @@ -256,7 +245,7 @@ # bold script chars in math alphabetic block - "𝓐-𝓩": # 0x1d4d0 - 0x1d4e9 - - tc: "BTs" + - tc: "BTsC" - spell: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'abcdefghijklmnopqrstuvwxyz')" - "𝓪-𝔃": # 0x1d4ea - 0x1d503 @@ -264,7 +253,7 @@ - spell: "translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz')" - "𝐀-𝐙": # 0x1d400 - 0x1d419 - - tc: "B" + - tc: "BC" - spell: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'abcdefghijklmnopqrstuvwxyz')" - "𝐚-𝐳": # 0x1d41a - 0x1d433 @@ -281,14 +270,16 @@ - spell: "translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔𝑕𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz')" - "𝑨-𝒁": # 0x1d468 - 0x1d481 - - tc: "BIC" + - tc: "BC" - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'abcdefghijklmnopqrstuvwxyz')" - "𝒂-𝒛": # 0x1d482 - 0x1d49b + - tc: "B" - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 - - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - tc: "C" + - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'abcdefghijklmnopqrstuvwxyz')" - "𝖺-𝗓": # 0x1d5ba - 0x1d5d3 - spell: "translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz')" @@ -296,8 +287,9 @@ - "𝗔-𝗭": # 0x1d5d4 - 0x1d5ed - test: if: "not($IgnoreBold)" - then: [tc: "B"] - - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + then: [tc: "BC"] + else: [tc: "C"] + - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'abcdefghijklmnopqrstuvwxyz')" - "𝗮-𝘇": # 0x1d5ee - 0x1d607 - test: @@ -306,27 +298,27 @@ - spell: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" - "𝘈-𝘡": # 0x1d608 - 0x1d621 - # - tc: "italic" - - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + # don't include italics + - tc: "C" + - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'abcdefghijklmnopqrstuvwxyz')" - "𝘢-𝘻": # 0x1d622 - 0x1d63b - # - tc: "italic" + # don't include italics - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" - "𝘼-𝙕": # 0x1d63c - 0x1d655 - # - tc: "bold italic" + # bold italic — UEB: no italic typeform; bold + capital only - test: - if: "$IgnoreBold" - then: [tc: I] - else: [tc: "BI"] - - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + if: "not($IgnoreBold)" + then: [tc: "BC"] + else: [tc: "C"] + - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'abcdefghijklmnopqrstuvwxyz')" - "𝙖-𝙯": # 0x1d656 - 0x1d66f - # - tc: "bold italic" + # bold italic — UEB: no italic typeform; bold only - test: - if: "$IgnoreBold" - then: [tc: I] - else: [tc: "BI"] + if: "not($IgnoreBold)" + then: [tc: "B"] - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" - "𝙰-𝚉": # 0x1d670 - 0x1d689 @@ -342,8 +334,9 @@ - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 - test: if: "not($IgnoreBold)" - then: [tc: "B"] - - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + then: [tc: "BC"] + else: [tc: "C"] + - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝛂-𝛚": # 0x1d6c2 - 0x1d6da - test: @@ -355,7 +348,7 @@ - test: if: "not($IgnoreBold)" then: [tc: "B"] - - spell: "translate('.', '𝜵', '∇')" + - spell: "translate('.', '𝛁', '∇')" - "𝛛𝛜𝛝𝛞𝛟𝛠𝛡": # 0x1D6DB - 0x1D6E1 - test: @@ -364,7 +357,7 @@ - spell: "translate('.', '𝛛𝛜𝛝𝛞𝛟𝛠𝛡', '∂εθκφρπ')" - "𝛢-𝛺": # 0x1d6e2 - 0x1d6fa - - tc: "IC" + - tc: "C" - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹𝛺', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝛼-𝜔": # 0x1d6fc - 0x1d714 @@ -372,7 +365,7 @@ - "𝛻": # 0x1d6fb - - spell: "translate('.', '𝜵', '∇')" + - spell: "translate('.', '𝛻', '∇')" - "𝜕𝜖𝜗𝜘𝜙𝜚𝜛": # 0x1d715 - 0x1d71b # - tc: "italic" @@ -380,19 +373,19 @@ - "𝜜-𝜴": # 0x1d71c - 0x1d734 # - tc: "bold italic" - - tc: 'BIC⠁' + - tc: "BC" - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳𝜴', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝜶-𝝎": # 0x1d736 - 0x1d74e - - tc: "BI" + - tc: "B" - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆𝝇𝝈𝝉𝝊𝝋𝝌𝝍𝝎', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝝏𝝐𝝑𝝒𝝓𝝔𝝕": # 0x1d74f - 0x1d755 - - tc: "BI" + - tc: "B" - spell: "translate('.', '𝝏𝝐𝝑𝝒𝝓𝝔𝝕', '∂εκθφρπ')" - "𝜵": # 0x1d735 - - tc: "BI" + - tc: "B" - spell: "translate('.', '𝜵', '∇')" - "𝝖-𝝮": # 0x1d756 - 0x1d76e @@ -409,22 +402,21 @@ - "𝝯": # 0x1d76f - tc: "B" - - spell: "translate('.', '𝜵', '∇')" + - spell: "translate('.', '𝝯', '∇')" - "𝞐-𝞨": # 0x1d790 - 0x1d7a8 - # - tc: "bold italic" + # bold italic — UEB: no italic typeform; bold + capital only - test: - if: "$IgnoreBold" - then: [tc: I] - else: [tc: "BI"] - - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + if: "not($IgnoreBold)" + then: [tc: "BC"] + else: [tc: "C"] + - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝞪-𝟂": # 0x1d7aa - 0x1d7c2 - # - tc: "bold italic" + # bold italic — UEB: no italic typeform; bold only - test: - if: "$IgnoreBold" - then: [tc: I] - else: [tc: "BI"] + if: "not($IgnoreBold)" + then: [tc: "B"] - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺𝞻𝞼𝞽𝞾𝞿𝟀𝟁𝟂', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝟃𝟄𝟅𝟆𝟇𝟈𝟉": # 0x1d7c3 - 0x1d7c9 diff --git a/Rules/Braille/UEB/unicode.yaml b/Rules/Braille/UEB/unicode.yaml index e1b513bc8..ccee08652 100644 --- a/Rules/Braille/UEB/unicode.yaml +++ b/Rules/Braille/UEB/unicode.yaml @@ -367,84 +367,17 @@ - "y": [t: "L⠽"] # 0x79 - "z": [t: "L⠵"] # 0x7a - - "A": [t: "CL⠁"] # 0x41 - - "B": # 0x42 + # Latin capitals: chem-element letters use 𝐶 (no word indicator); others use C. + # Cells come from lowercase a-z via spell/translate. + - "BCHIKNOPSUVWY": # chemistry-capable capitals - test: if: "@data-chem-element" - then: [t: "𝐶L⠃"] - else: [t: "CL⠃"] - - "C": # 0x43 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠉"] - else: [t: "CL⠉"] - - "D": [t: "CL⠙"] # 0x44 - - "E": [t: "CL⠑"] # 0x45 - - "F": [t: "CL⠋"] # 0x46 - - "G": [t: "CL⠛"] # 0x47 - - "H": # 0x48 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠓"] - else: [t: "CL⠓"] - - "I": # 0x49 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠊"] - else: [t: "CL⠊"] - - "J": [t: "CL⠚"] # 0x4a - - "K": # 0x4b - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠅"] - else: [t: "CL⠅"] - - "L": [t: "CL⠇"] # 0x4c - - "M": [t: "CL⠍"] # 0x4d - - "N": # 0x4e - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠝"] - else: [t: "CL⠝"] - - "O": # 0x4f - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠕"] - else: [t: "CL⠕"] - - "P": # 0x50 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠏"] - else: [t: "CL⠏"] - - "Q": [t: "CL⠟"] # 0x51 - - "R": [t: "CL⠗"] # 0x52 - - "S": # 0x53 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠎"] - else: [t: "CL⠎"] - - "T": [t: "CL⠞"] # 0x54 - - "U": # 0x55 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠥"] - else: [t: "CL⠥"] - - "V": # 0x56 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠧"] - else: [t: "CL⠧"] - - "W": # 0x57 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠺"] - else: [t: "CL⠺"] - - "X": [t: "CL⠭"] # 0x58 - - "Y": # 0x59 - - test: - if: "@data-chem-element" - then: [t: "𝐶L⠽"] - else: [t: "CL⠽"] - - "Z": [t: "CL⠵"] # 0x5a + then: [tc: "𝐶"] + else: [tc: "C"] + - spell: "translate('.', 'BCHIKNOPSUVWY', 'bchiknopsuvwy')" + - "ADEFGJLMQRTXZ": # remaining capitals + - tc: "C" + - spell: "translate('.', 'ADEFGJLMQRTXZ', 'adefgjlmqrtxz')" - "α": [t: "GL⠁"] # 0x3b1 - "β": [t: "GL⠃"] # 0x3b2 @@ -486,7 +419,7 @@ - "Δ": [t: "CGL⠙"] # 0x394 - "Ε": [t: "CGL⠑"] # 0x395 - "Ζ": [t: "CGL⠵"] # 0x396 - - "Η": [t: "CGL⠱ "] # 0x397 + - "Η": [t: "CGL⠱"] # 0x397 - "Θ": [t: "CGL⠹"] # 0x398 - "Ι": [t: "CGL⠊"] # 0x399 - "Κ": [t: "CGL⠅"] # 0x39a diff --git a/Rules/Braille/Vietnam/unicode-full.yaml b/Rules/Braille/Vietnam/unicode-full.yaml index eba63e7f3..43008ef7b 100644 --- a/Rules/Braille/Vietnam/unicode-full.yaml +++ b/Rules/Braille/Vietnam/unicode-full.yaml @@ -24,8 +24,8 @@ - "↉": [t: "#N⠚N⠌N⠑"] # 0x2189 (Vulgar Fraction Zero Thirds) - 𝐀-𝐙: # 0x1d400 - 0x1d419 - - tc: "B" - - spell: translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - tc: "BC" + - spell: translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'abcdefghijklmnopqrstuvwxyz') - 𝐚-𝐳: # 0x1d41a - 0x1d433 - tc: "B" @@ -33,38 +33,37 @@ - 𝐴-𝑍: # 0x1d434 - 0x1d44d - tc: "C" - - spell: translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - spell: translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'abcdefghijklmnopqrstuvwxyz') - 𝑎-𝑧: # 0x1d44e - 0x1d467 - - tc: "L" - spell: translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔𝑕𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz') - 𝒂-𝒛: # 0x1d482 - 0x1d49b - - tc: "BI" + - tc: "B" - spell: translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz') - 𝑨-𝒁: # 0x1d468 - 0x1d481 - - tc: "BI" - - spell: translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - tc: "BC" + - spell: translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'abcdefghijklmnopqrstuvwxyz') - 𝒶-𝓏: # 0x1d4b6 - 0x1d4cf - tc: "Ts" - spell: translate('.', '𝒶𝒷𝒸𝒹𝒺𝒻𝒼𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝓄𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏', 'abcdefghijklmnopqrstuvwxyz') - 𝒜-𝒵: # 0x1d49c - 0x1d4b5 - - tc: "Ts" - - spell: translate('.', '𝒜𝒝𝒞𝒟𝒠𝒡𝒢𝒣𝒤𝒥𝒦𝒧𝒨𝒩𝒪𝒫𝒬𝒭𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - tc: "TsC" + - spell: translate('.', '𝒜𝒝𝒞𝒟𝒠𝒡𝒢𝒣𝒤𝒥𝒦𝒧𝒨𝒩𝒪𝒫𝒬𝒭𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'abcdefghijklmnopqrstuvwxyz') - 𝓐-𝓩: # 0x1d4d0 - 0x1d4e9 - - tc: "BTs" - - spell: translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - tc: "BTsC" + - spell: translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'abcdefghijklmnopqrstuvwxyz') - 𝓪-𝔃: # 0x1d4ea - 0x1d503 - tc: "BTs" - spell: translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz') - 𝔄-𝔝: # 0x1d504 - 0x1d51d - - tc: "D" - - spell: translate('.', '𝔄𝔅𝔆𝔇𝔈𝔉𝔊𝔋𝔌𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔕𝔖𝔗𝔘𝔙𝔚𝔛𝔜𝔝', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - tc: "DC" + - spell: translate('.', '𝔄𝔅𝔆𝔇𝔈𝔉𝔊𝔋𝔌𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔕𝔖𝔗𝔘𝔙𝔚𝔛𝔜𝔝', 'abcdefghijklmnopqrstuvwxyz') - 𝔞-𝔷: # 0x1d51e - 0x1d537 - tc: "D" - spell: translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz') @@ -82,14 +81,14 @@ - "ℴ": [t: "TL⠕"] # 0x21334 (Script Small O) - 𝔸-𝔹: # 0x1d538 - 0x1d550 - - tc: "𝔹" - - spell: translate('.', '𝔸𝔹', 'AB') + - tc: "𝔹C" + - spell: translate('.', '𝔸𝔹', 'ab') - 𝔻-𝔾: - - tc: "𝔹" - - spell: translate('.', '𝔻𝔼𝔽𝔾', 'DEFG') + - tc: "𝔹C" + - spell: translate('.', '𝔻𝔼𝔽𝔾', 'defg') - 𝕀-𝕄: - - tc: "𝔹" - - spell: translate('.', '𝕀𝕁𝕂𝕃𝕄', 'IJKLM') + - tc: "𝔹C" + - spell: translate('.', '𝕀𝕁𝕂𝕃𝕄', 'ijklm') - "ℂ": [t: "𝔹CL⠉"] # 0x2102 - "ℍ": [t: "𝔹CL⠓"] # 0x210d @@ -98,14 +97,14 @@ - 𝕊-𝕐: - t: "𝔹" - - spell: translate('.', '𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'STUVWXY') + - spell: translate('.', '𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'stuvwxy') - 𝕒-𝕫: # 0x1d552 - 0x1d56b - t: "𝔹" - spell: translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz') - 𝕬-𝖅: - t: "BD" - - spell: translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - spell: translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'abcdefghijklmnopqrstuvwxyz') - "ℌ": [t: "DCL⠓"] # 0x210C (Fraktur Capital H) - "ℑ": [t: "DCL⠊"] # 0x2111 (Fraktur Capital I) @@ -117,32 +116,32 @@ - tc: "BD" - spell: translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz') - 𝖠-𝖹: # 0x1d5a0 - 0x1d5b9 - - tc: "S" - - spell: translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - tc: "SC" + - spell: translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'abcdefghijklmnopqrstuvwxyz') - 𝖺-𝗓: # 0x1d5ba - 0x1d5d3 - tc: "S" - spell: translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz') - 𝗔-𝗭: # 0x1d5d4 - 0x1d5ed - - tc: "BS" - - spell: translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - tc: "BSC" + - spell: translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'abcdefghijklmnopqrstuvwxyz') - 𝗮-𝘇: # 0x1d5ee - 0x1d607 - tc: "BS" - spell: translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz') - 𝘈-𝘡: # 0x1d608 - 0x1d621 - t: "IS" - - spell: translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - spell: translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'abcdefghijklmnopqrstuvwxyz') - 𝘢-𝘻: # 0x1d622 - 0x1d63b - t: "IS" - spell: translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz') - 𝘼-𝙕: # 0x1d63c - 0x1d655 - t: "BILS" - - spell: translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - spell: translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'abcdefghijklmnopqrstuvwxyz') - 𝙖-𝙯: # 0x1d656 - 0x1d66f - t: "BILS" - spell: translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz') - 𝙰-𝚉: # 0x1d670 - 0x1d689 - - spell: translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + - spell: translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'abcdefghijklmnopqrstuvwxyz') - 𝚊-𝚣: # 0x1d68a - 0x1d6a3 - spell: translate('.', '𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣', 'abcdefghijklmnopqrstuvwxyz') @@ -151,40 +150,40 @@ - 𝚨-𝚿: # 0x1d6a8 - 0x1d6bf - tc: "BCG" - - spell: translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ') + - spell: translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝛂-𝛙: # 0x1d6c2 - 0x1d6d9 - tc: "BG" - spell: translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒𝛓𝛔𝛕𝛖𝛗𝛘𝛙', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝛢-𝛹: # 0x1d6e2 - 0x1d6f9 - - tc: "ICG" - - spell: translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ') + - tc: "CG" + - spell: translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝛼-𝜓: # 0x1d6fc - 0x1d713 - - tc: "IG" + - tc: "G" - spell: translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌𝜍𝜎𝜏𝜐𝜑𝜒𝜓', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝜜-𝜳: # 0x1d71c - 0x1d733 - - tc: "BICG" - - spell: translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ') + - tc: "BCG" + - spell: translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝜶-𝝍: # 0x1d736 - 0x1d74d - - tc: "BIG" + - tc: "BG" - spell: translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆𝝇𝝈𝝉𝝊𝝋𝝌𝝍', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝝖-𝝭: # 0x1d756 - 0x1d76d - tc: "BSCG" - - spell: translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ') + - spell: translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝝰-𝞇: # 0x1d770 - 0x1d787 - tc: "BSG" - spell: translate('.', '𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿𝞀𝞁𝞂𝞃𝞄𝞅𝞆𝞇', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝞐-𝞧: # 0x1d790 - 0x1d7a7 - - tc: "BISCG" - - spell: translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ') + - tc: "BSCG" + - spell: translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝞪-𝟁: # 0x1d7aa - 0x1d7c1 - - tc: "BISG" + - tc: "BSG" - spell: translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺𝞻𝞼𝞽𝞾𝞿𝟀𝟁', 'αβγδεζηθικλμνξοπρςστυφχψω') - 𝟎-𝟗: # 0x1d7ce - 0x1d7d7 - tc: "BN" - spell: translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789') - 𝟘-𝟡: # 0x1d7d8 - 0x1d7e1 - - tc: "𝔹N" + - tc: "𝔹NC" - spell: translate('.', '𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡', '0123456789') - 𝟢-𝟫: # 0x1d7e2 - 0x1d7eb - tc: "SN" diff --git a/Rules/Braille/Vietnam/unicode.yaml b/Rules/Braille/Vietnam/unicode.yaml index 2d9a6150a..78a9adaa6 100644 --- a/Rules/Braille/Vietnam/unicode.yaml +++ b/Rules/Braille/Vietnam/unicode.yaml @@ -511,32 +511,10 @@ - "Ỹ": [t: "CL⠤𝑐L⠽"] - "Ỵ": [t: "CL⠠𝑐L⠽"] - - "A": [t: "CL⠁"] # 0x41 - - "B": [t: "CL⠃"] # 0x42 - - "C": [t: "CL⠉"] # 0x43 - - "D": [t: "CL⠙"] # 0x44 - - "E": [t: "CL⠑"] # 0x45 - - "F": [t: "CL⠋"] # 0x46 - - "G": [t: "CL⠛"] # 0x47 - - "H": [t: "CL⠓"] # 0x48 - - "I": [t: "CL⠊"] # 0x49 - - "J": [t: "CL⠚"] # 0x4a - - "K": [t: "CL⠅"] # 0x4b - - "L": [t: "CL⠇"] # 0x4c - - "M": [t: "CL⠍"] # 0x4d - - "N": [t: "CL⠝"] # 0x4e - - "O": [t: "CL⠕"] # 0x4f - - "P": [t: "CL⠏"] # 0x50 - - "Q": [t: "CL⠟"] # 0x51 - - "R": [t: "CL⠗"] # 0x52 - - "S": [t: "CL⠎"] # 0x53 - - "T": [t: "CL⠞"] # 0x54 - - "U": [t: "CL⠥"] # 0x55 - - "V": [t: "CL⠧"] # 0x56 - - "W": [t: "CL⠺"] # 0x57 - - "X": [t: "CL⠭"] # 0x58 - - "Y": [t: "CL⠽"] # 0x59 - - "Z": [t: "CL⠵"] # 0x5a + # Latin capitals: C + cells from lowercase a-z via spell/translate. + - "A-Z": # 0x41 - 0x5a + - tc: "C" + - spell: "translate('.', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')" - "α": [t: "GL⠁"] # 0x3b1 - "β": [t: "GL⠃"] # 0x3b2 diff --git a/Rules/Languages/id/unicode-full.yaml b/Rules/Languages/id/unicode-full.yaml index c1467a28e..5591060fd 100644 --- a/Rules/Languages/id/unicode-full.yaml +++ b/Rules/Languages/id/unicode-full.yaml @@ -3846,6469 +3846,584 @@ - "": [T: integral terbalik] # 0xef83 - "": [T: nol ganda di atas nol ganda] # 0xef90 - "": [T: nol dengan garis miring] # 0xef91 - - "": # 0xf000 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "𝔄": # 0x1d504 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "": # 0xf001 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "𝔅": # 0x1d505 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "": # 0xf003 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "𝔇": # 0x1d507 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "": # 0xf004 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "𝔈": # 0x1d508 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "": # 0xf005 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "𝔉": # 0x1d509 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "": # 0xf006 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "𝔊": # 0x1d50a - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "": # 0xf009 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "𝔍": # 0x1d50d - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "": # 0xf00a - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "𝔎": # 0x1d50e - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "": # 0xf00b - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "𝔏": # 0x1d50f - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "": # 0xf00c - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "𝔐": # 0x1d510 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "": # 0xf00d - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "𝔑": # 0x1d511 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "": # 0xf00e - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "𝔒": # 0x1d512 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "": # 0xf00f - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "𝔓": # 0x1d513 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "": # 0xf010 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "𝔔": # 0x1d514 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "": # 0xf012 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "𝔖": # 0x1d516 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "": # 0xf013 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "𝔗": # 0x1d517 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "": # 0xf014 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "𝔘": # 0x1d518 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "": # 0xf015 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "𝔙": # 0x1d519 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "": # 0xf016 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "𝔚": # 0x1d51a - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "": # 0xf017 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "𝔛": # 0x1d51b - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "": # 0xf018 - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "𝔜": # 0x1d51c - - T: fraktur - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "": [T: fraktur a] # 0xf01a - - "𝔞": [T: fraktur a] # 0x1d51e - - "": [T: fraktur b] # 0xf01b - - "𝔟": [T: fraktur b] # 0x1d51f - - "": [T: fraktur c] # 0xf01c - - "𝔠": [T: fraktur c] # 0x1d520 - - "": [T: fraktur d] # 0xf01d - - "𝔡": [T: fraktur d] # 0x1d521 - - "": [T: fraktur e] # 0xf01e - - "𝔢": [T: fraktur e] # 0x1d522 - - "": [T: fraktur f] # 0xf01f - - "𝔣": [T: fraktur f] # 0x1d523 - - "": [T: fraktur g] # 0xf020 - - "𝔤": [T: fraktur g] # 0x1d524 - - "": [T: fraktur h] # 0xf021 - - "𝔥": [T: fraktur h] # 0x1d525 - - "": [T: fraktur i] # 0xf022 - - "𝔦": [T: fraktur i] # 0x1d526 - - "": [T: fraktur j] # 0xf023 - - "𝔧": [T: fraktur j] # 0x1d527 - - "": [T: fraktur k] # 0xf024 - - "𝔨": [T: fraktur k] # 0x1d528 - - "": [T: fraktur l] # 0xf025 - - "𝔩": [T: fraktur l] # 0x1d529 - - "": [T: fraktur m] # 0xf026 - - "𝔪": [T: fraktur m] # 0x1d52a - - "": [T: fraktur n] # 0xf027 - - "𝔫": [T: fraktur n] # 0x1d52b - - "": [T: fraktur o] # 0xf028 - - "𝔬": [T: fraktur o] # 0x1d52c - - "": [T: fraktur p] # 0xf029 - - "𝔭": [T: fraktur p] # 0x1d52d - - "": [T: fraktur q] # 0xf02a - - "𝔮": [T: fraktur q] # 0x1d52e - - "": [T: fraktur r] # 0xf02b - - "𝔯": [T: fraktur r] # 0x1d52f - - "": [T: fraktur s] # 0xf02c - - "𝔰": [T: fraktur s] # 0x1d530 - - "": [T: fraktur t] # 0xf02d - - "𝔱": [T: fraktur t] # 0x1d531 - - "": [T: fraktur u] # 0xf02e - - "𝔲": [T: fraktur u] # 0x1d532 - - "": [T: fraktur v] # 0xf02f - - "𝔳": [T: fraktur v] # 0x1d533 - - "": [T: fraktur w] # 0xf030 - - "𝔴": [T: fraktur w] # 0x1d534 - - "": [T: fraktur x] # 0xf031 - - "𝔵": [T: fraktur x] # 0x1d535 - - "": [T: fraktur y] # 0xf032 - - "𝔶": [T: fraktur y] # 0x1d536 - - "": [T: fraktur z] # 0xf033 - - "𝔷": [T: fraktur z] # 0x1d537 - - "": # 0xf040 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "𝕬": # 0x1d56c - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "": # 0xf041 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "𝕭": # 0x1d56d - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "": # 0xf042 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "𝕮": # 0x1d56e - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "": # 0xf043 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "𝕯": # 0x1d56f - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "": # 0xf044 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "𝕰": # 0x1d570 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "": # 0xf045 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "𝕱": # 0x1d571 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "": # 0xf046 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "𝕲": # 0x1d572 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "": # 0xf047 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "𝕳": # 0x1d573 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "": # 0xf048 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "𝕴": # 0x1d574 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "": # 0xf049 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "𝕵": # 0x1d575 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "": # 0xf04a - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "𝕶": # 0x1d576 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "": # 0xf04b - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "𝕷": # 0x1d577 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "": # 0xf04c - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "𝕸": # 0x1d578 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "": # 0xf04d - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "𝕹": # 0x1d579 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "": # 0xf04e - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "𝕺": # 0x1d57a - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "": # 0xf04f - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "𝕻": # 0x1d57b - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "": # 0xf050 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "𝕼": # 0x1d57c - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "": # 0xf051 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "𝕽": # 0x1d57d - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "": # 0xf052 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "𝕾": # 0x1d57e - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "": # 0xf053 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "𝕿": # 0x1d57f - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "": # 0xf054 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "𝖀": # 0x1d580 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "": # 0xf055 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "𝖁": # 0x1d581 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "": # 0xf056 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "𝖂": # 0x1d582 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "": # 0xf057 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "𝖃": # 0x1d583 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "": # 0xf058 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "𝖄": # 0x1d584 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "": # 0xf059 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "𝖅": # 0x1d585 - - T: fraktur bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "": [T: fraktur bold a] # 0xf05a - - "𝖆": [T: fraktur bold a] # 0x1d586 - - "": [T: fraktur bold b] # 0xf05b - - "𝖇": [T: fraktur bold b] # 0x1d587 - - "": [T: fraktur bold c] # 0xf05c - - "𝖈": [T: fraktur bold c] # 0x1d588 - - "": [T: fraktur bold d] # 0xf05d - - "𝖉": [T: fraktur bold d] # 0x1d589 - - "": [T: fraktur bold e] # 0xf05e - - "𝖊": [T: fraktur bold e] # 0x1d58a - - "": [T: fraktur bold f] # 0xf05f - - "𝖋": [T: fraktur bold f] # 0x1d58b - - "": [T: fraktur bold g] # 0xf060 - - "𝖌": [T: fraktur bold g] # 0x1d58c - - "": [T: fraktur bold h] # 0xf061 - - "𝖍": [T: fraktur bold h] # 0x1d58d - - "": [T: fraktur bold i] # 0xf062 - - "𝖎": [T: fraktur bold i] # 0x1d58e - - "": [T: fraktur bold j] # 0xf063 - - "𝖏": [T: fraktur bold j] # 0x1d58f - - "": [T: fraktur bold k] # 0xf064 - - "𝖐": [T: fraktur bold k] # 0x1d590 - - "": [T: fraktur bold l] # 0xf065 - - "𝖑": [T: fraktur bold l] # 0x1d591 - - "": [T: fraktur bold m] # 0xf066 - - "𝖒": [T: fraktur bold m] # 0x1d592 - - "": [T: fraktur bold n] # 0xf067 - - "𝖓": [T: fraktur bold n] # 0x1d593 - - "": [T: fraktur bold o] # 0xf068 - - "𝖔": [T: fraktur bold o] # 0x1d594 - - "": [T: fraktur bold p] # 0xf069 - - "𝖕": [T: fraktur bold p] # 0x1d595 - - "": [T: fraktur bold q] # 0xf06a - - "𝖖": [T: fraktur bold q] # 0x1d596 - - "": [T: fraktur bold r] # 0xf06b - - "𝖗": [T: fraktur bold r] # 0x1d597 - - "": [T: fraktur bold s] # 0xf06c - - "𝖘": [T: fraktur bold s] # 0x1d598 - - "": [T: fraktur bold t] # 0xf06d - - "𝖙": [T: fraktur bold t] # 0x1d599 - - "": [T: fraktur bold u] # 0xf06e - - "𝖚": [T: fraktur bold u] # 0x1d59a - - "": [T: fraktur bold v] # 0xf06f - - "𝖛": [T: fraktur bold v] # 0x1d59b - - "": [T: fraktur bold w] # 0xf070 - - "𝖜": [T: fraktur bold w] # 0x1d59c - - "": [T: fraktur bold x] # 0xf071 - - "𝖝": [T: fraktur bold x] # 0x1d59d - - "": [T: fraktur bold y] # 0xf072 - - "𝖞": [T: fraktur bold y] # 0x1d59e - - "": [T: fraktur bold z] # 0xf073 - - "𝖟": [T: fraktur bold z] # 0x1d59f - - "": # 0xf080 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "": # 0xf081 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "𝔹": # 0x1d539 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "": # 0xf083 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "𝔻": # 0x1d53b - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "": # 0xf084 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "𝔼": # 0x1d53c - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "": # 0xf085 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "𝔽": # 0x1d53d - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "": # 0xf086 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "𝔾": # 0x1d53e - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "": # 0xf088 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "𝕀": # 0x1d540 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "": # 0xf089 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "𝕁": # 0x1d541 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "": # 0xf08a - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "𝕂": # 0x1d542 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "": # 0xf08b - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "𝕃": # 0x1d543 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "": # 0xf08c - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "𝕄": # 0x1d544 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "": # 0xf08e - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "𝕆": # 0x1d546 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "": # 0xf092 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "𝕊": # 0x1d54a - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "": # 0xf093 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "𝕋": # 0x1d54b - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "": # 0xf094 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "𝕌": # 0x1d54c - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "": # 0xf095 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "𝕍": # 0x1d54d - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "": # 0xf096 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "𝕎": # 0x1d54e - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "": # 0xf097 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "𝕏": # 0x1d54f - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "": # 0xf098 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "𝕐": # 0x1d550 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "": [T: struck ganda a] # 0xf09a - - "𝕒": [T: struck ganda a] # 0x1d552 - - "": [T: struck ganda b] # 0xf09b - - "𝕓": [T: struck ganda b] # 0x1d553 - - "": [T: struck ganda c] # 0xf09c - - "𝕔": [T: struck ganda c] # 0x1d554 - - "": [T: struck ganda d] # 0xf09d - - "𝕕": [T: struck ganda d] # 0x1d555 - - "": [T: struck ganda e] # 0xf09e - - "𝕖": [T: struck ganda e] # 0x1d556 - - "": [T: struck ganda f] # 0xf09f - - "𝕗": [T: struck ganda f] # 0x1d557 - - "": [T: struck ganda g] # 0xf0a0 - - "𝕘": [T: struck ganda g] # 0x1d558 - - "": [T: struck ganda h] # 0xf0a1 - - "𝕙": [T: struck ganda h] # 0x1d559 - - "": [T: struck ganda i] # 0xf0a2 - - "𝕚": [T: struck ganda i] # 0x1d55a - - "": [T: struck ganda j] # 0xf0a3 - - "𝕛": [T: struck ganda j] # 0x1d55b - - "": [T: struck ganda k] # 0xf0a4 - - "𝕜": [T: struck ganda k] # 0x1d55c - - "": [T: struck ganda l] # 0xf0a5 - - "𝕝": [T: struck ganda l] # 0x1d55d - - "": [T: struck ganda m] # 0xf0a6 - - "𝕞": [T: struck ganda m] # 0x1d55e - - "": [T: struck ganda n] # 0xf0a7 - - "𝕟": [T: struck ganda n] # 0x1d55f - - "": [T: struck ganda o] # 0xf0a8 - - "𝕠": [T: struck ganda o] # 0x1d560 - - "": [T: struck ganda p] # 0xf0a9 - - "𝕡": [T: struck ganda p] # 0x1d561 - - "": [T: struck ganda q] # 0xf0aa - - "𝕢": [T: struck ganda q] # 0x1d562 - - "": [T: struck ganda r] # 0xf0ab - - "𝕣": [T: struck ganda r] # 0x1d563 - - "": [T: struck ganda s] # 0xf0ac - - "𝕤": [T: struck ganda s] # 0x1d564 - - "": [T: struck ganda t] # 0xf0ad - - "𝕥": [T: struck ganda t] # 0x1d565 - - "": [T: struck ganda u] # 0xf0ae - - "𝕦": [T: struck ganda u] # 0x1d566 - - "": [T: struck ganda v] # 0xf0af - - "𝕧": [T: struck ganda v] # 0x1d567 - - "": [T: struck ganda w] # 0xf0b0 - - "𝕨": [T: struck ganda w] # 0x1d568 - - "": [T: struck ganda x] # 0xf0b1 - - "𝕩": [T: struck ganda x] # 0x1d569 - - "": [T: struck ganda y] # 0xf0b2 - - "𝕪": [T: struck ganda y] # 0x1d56a - - "": [T: struck ganda z] # 0xf0b3 - - "𝕫": [T: struck ganda z] # 0x1d56b - - "": [T: struck ganda nol] # 0xf0c0 - - "𝟘": [T: struck ganda nol] # 0x1d7d8 - - "": [T: struck ganda satu] # 0xf0c1 - - "𝟙": [T: struck ganda satu] # 0x1d7d9 - - "": [T: struck ganda dua] # 0xf0c2 - - "𝟚": [T: struck ganda dua] # 0x1d7da - - "": [T: struck ganda tiga] # 0xf0c3 - - "𝟛": [T: struck ganda tiga] # 0x1d7db - - "": [T: struck ganda empat] # 0xf0c4 - - "𝟜": [T: struck ganda empat] # 0x1d7dc - - "": [T: struck ganda lima] # 0xf0c5 - - "𝟝": [T: struck ganda lima] # 0x1d7dd - - "": [T: struck ganda enam] # 0xf0c6 - - "𝟞": [T: struck ganda enam] # 0x1d7de - - "": [T: struck ganda tujuh] # 0xf0c7 - - "𝟟": [T: struck ganda tujuh] # 0x1d7df - - "": [T: struck ganda delapan] # 0xf0c8 - - "𝟠": [T: struck ganda delapan] # 0x1d7e0 - - "": [T: struck ganda sembilan] # 0xf0c9 - - "𝟡": [T: struck ganda sembilan] # 0x1d7e1 - - "": [T: struck ganda nabla] # 0xf0ca - - "": [T: struck ganda konstanta euler] # 0xf0cb - - "": # 0xf100 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "𝒜": # 0x1d49c - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "": # 0xf102 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "𝒞": # 0x1d49e - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "": # 0xf103 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "𝒟": # 0x1d49f - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "": # 0xf106 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "𝒢": # 0x1d4a2 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "": # 0xf109 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "𝒥": # 0x1d4a5 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "": # 0xf10a - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "𝒦": # 0x1d4a6 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "": # 0xf10d - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "𝒩": # 0x1d4a9 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "": # 0xf10e - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "𝒪": # 0x1d4aa - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "": # 0xf10f - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "𝒫": # 0x1d4ab - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "": # 0xf110 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "𝒬": # 0x1d4ac - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "": # 0xf112 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "𝒮": # 0x1d4ae - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "": # 0xf113 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "𝒯": # 0x1d4af - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "": # 0xf114 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "𝒰": # 0x1d4b0 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "": # 0xf115 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "𝒱": # 0x1d4b1 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "": # 0xf116 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "𝒲": # 0x1d4b2 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "": # 0xf117 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "𝒳": # 0x1d4b3 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "": # 0xf118 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "𝒴": # 0x1d4b4 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "": # 0xf119 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "𝒵": # 0x1d4b5 - - T: aksara - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "": [T: aksara a] # 0xf11a - - "𝒶": [T: aksara a] # 0x1d4b6 - - "": [T: aksara b] # 0xf11b - - "𝒷": [T: aksara b] # 0x1d4b7 - - "": [T: aksara c] # 0xf11c - - "𝒸": [T: aksara c] # 0x1d4b8 - - "": [T: aksara d] # 0xf11d - - "𝒹": [T: aksara d] # 0x1d4b9 - - "": [T: aksara f] # 0xf11f - - "𝒻": [T: aksara f] # 0x1d4bb - - "": [T: aksara h] # 0xf121 - - "𝒽": [T: aksara h] # 0x1d4bd - - "": [T: aksara i] # 0xf122 - - "𝒾": [T: aksara i] # 0x1d4be - - "": [T: aksara j] # 0xf123 - - "𝒿": [T: aksara j] # 0x1d4bf - - "": [T: aksara k] # 0xf124 - - "𝓀": [T: aksara k] # 0x1d4c0 - - "": [T: aksara m] # 0xf126 - - "𝓂": [T: aksara m] # 0x1d4c2 - - "": [T: aksara n] # 0xf127 - - "𝓃": [T: aksara n] # 0x1d4c3 - - "": [T: aksara p] # 0xf129 - - "𝓅": [T: aksara p] # 0x1d4c5 - - "": [T: aksara q] # 0xf12a - - "𝓆": [T: aksara q] # 0x1d4c6 - - "": [T: aksara r] # 0xf12b - - "𝓇": [T: aksara r] # 0x1d4c7 - - "": [T: aksara s] # 0xf12c - - "𝓈": [T: aksara s] # 0x1d4c8 - - "": [T: aksara t] # 0xf12d - - "𝓉": [T: aksara t] # 0x1d4c9 - - "": [T: aksara u] # 0xf12e - - "𝓊": [T: aksara u] # 0x1d4ca - - "": [T: aksara v] # 0xf12f - - "𝓋": [T: aksara v] # 0x1d4cb - - "": [T: aksara w] # 0xf130 - - "𝓌": [T: aksara w] # 0x1d4cc - - "": [T: aksara x] # 0xf131 - - "𝓍": [T: aksara x] # 0x1d4cd - - "": [T: aksara y] # 0xf132 - - "𝓎": [T: aksara y] # 0x1d4ce - - "": [T: aksara z] # 0xf133 - - "𝓏": [T: aksara z] # 0x1d4cf - - "": # 0xf140 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "𝓐": # 0x1d4d0 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "": # 0xf141 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "𝓑": # 0x1d4d1 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "": # 0xf142 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "𝓒": # 0x1d4d2 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "": # 0xf143 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "𝓓": # 0x1d4d3 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "": # 0xf144 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "𝓔": # 0x1d4d4 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "": # 0xf145 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "𝓕": # 0x1d4d5 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "": # 0xf146 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "𝓖": # 0x1d4d6 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "": # 0xf147 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "𝓗": # 0x1d4d7 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "": # 0xf148 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "𝓘": # 0x1d4d8 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "": # 0xf149 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "𝓙": # 0x1d4d9 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "": # 0xf14a - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "𝓚": # 0x1d4da - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "": # 0xf14b - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "𝓛": # 0x1d4db - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "": # 0xf14c - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "𝓜": # 0x1d4dc - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "": # 0xf14d - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "𝓝": # 0x1d4dd - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "": # 0xf14e - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "𝓞": # 0x1d4de - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "": # 0xf14f - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "𝓟": # 0x1d4df - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "": # 0xf150 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "𝓠": # 0x1d4e0 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "": # 0xf151 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "𝓡": # 0x1d4e1 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "": # 0xf152 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "𝓢": # 0x1d4e2 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "": # 0xf153 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "𝓣": # 0x1d4e3 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "": # 0xf154 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "𝓤": # 0x1d4e4 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "": # 0xf155 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "𝓥": # 0x1d4e5 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "": # 0xf156 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "𝓦": # 0x1d4e6 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "": # 0xf157 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "𝓧": # 0x1d4e7 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "": # 0xf158 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "𝓨": # 0x1d4e8 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "": # 0xf159 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "𝓩": # 0x1d4e9 - - T: aksara bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "": [T: aksara bold a] # 0xf15a - - "𝓪": [T: aksara bold a] # 0x1d4ea - - "": [T: aksara bold b] # 0xf15b - - "𝓫": [T: aksara bold b] # 0x1d4eb - - "": [T: aksara bold c] # 0xf15c - - "𝓬": [T: aksara bold c] # 0x1d4ec - - "": [T: aksara bold d] # 0xf15d - - "𝓭": [T: aksara bold d] # 0x1d4ed - - "": [T: aksara bold e] # 0xf15e - - "𝓮": [T: aksara bold e] # 0x1d4ee - - "": [T: aksara bold f] # 0xf15f - - "𝓯": [T: aksara bold f] # 0x1d4ef - - "": [T: aksara bold g] # 0xf160 - - "𝓰": [T: aksara bold g] # 0x1d4f0 - - "": [T: aksara bold h] # 0xf161 - - "𝓱": [T: aksara bold h] # 0x1d4f1 - - "": [T: aksara bold i] # 0xf162 - - "𝓲": [T: aksara bold i] # 0x1d4f2 - - "": [T: aksara bold j] # 0xf163 - - "𝓳": [T: aksara bold j] # 0x1d4f3 - - "": [T: aksara bold k] # 0xf164 - - "𝓴": [T: aksara bold k] # 0x1d4f4 - - "": [T: aksara bold l] # 0xf165 - - "𝓵": [T: aksara bold l] # 0x1d4f5 - - "": [T: aksara bold m] # 0xf166 - - "𝓶": [T: aksara bold m] # 0x1d4f6 - - "": [T: aksara bold n] # 0xf167 - - "𝓷": [T: aksara bold n] # 0x1d4f7 - - "": [T: aksara bold o] # 0xf168 - - "𝓸": [T: aksara bold o] # 0x1d4f8 - - "": [T: aksara bold p] # 0xf169 - - "𝓹": [T: aksara bold p] # 0x1d4f9 - - "": [T: aksara bold q] # 0xf16a - - "𝓺": [T: aksara bold q] # 0x1d4fa - - "": [T: aksara bold r] # 0xf16b - - "𝓻": [T: aksara bold r] # 0x1d4fb - - "": [T: aksara bold s] # 0xf16c - - "𝓼": [T: aksara bold s] # 0x1d4fc - - "": [T: aksara bold t] # 0xf16d - - "𝓽": [T: aksara bold t] # 0x1d4fd - - "": [T: aksara bold u] # 0xf16e - - "𝓾": [T: aksara bold u] # 0x1d4fe - - "": [T: aksara bold v] # 0xf16f - - "𝓿": [T: aksara bold v] # 0x1d4ff - - "": [T: aksara bold w] # 0xf170 - - "𝔀": [T: aksara bold w] # 0x1d500 - - "": [T: aksara bold x] # 0xf171 - - "𝔁": [T: aksara bold x] # 0x1d501 - - "": [T: aksara bold y] # 0xf172 - - "𝔂": [T: aksara bold y] # 0x1d502 - - "": [T: aksara bold z] # 0xf173 - - "𝔃": [T: aksara bold z] # 0x1d503 - - "": # 0xf180 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "": # 0xf182 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "": # 0xf183 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "": # 0xf184 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "": # 0xf185 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "": # 0xf189 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "": # 0xf18a - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "": # 0xf18c - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "": # 0xf18e - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "": # 0xf18f - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "": # 0xf190 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "": # 0xf192 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "": # 0xf193 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "": # 0xf194 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "": # 0xf195 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "": # 0xf196 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "": # 0xf197 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "": # 0xf199 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "": # 0xf19a - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: ligature ae - - "": # 0xf19b - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: sharp s - - "": # 0xf19c - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o with stroke - - "": # 0xf201 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: delta - - "": # 0xf202 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: zai - - "": # 0xf203 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: lambda - - "": # 0xf204 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: pi - - "": # 0xf205 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: sigma - - "": # 0xf206 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "": # 0xf207 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: gamma - - "": # 0xf208 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omega - - "": # 0xf209 - - T: struck ganda - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: upsilon - - "": [T: struck ganda alpha] # 0xf220 - - "": [T: struck ganda beta] # 0xf221 - - "": [T: struck ganda phi] # 0xf222 - - "": [T: struck ganda zeta] # 0xf223 - - "": [T: struck ganda psi] # 0xf224 - - "": [T: struck ganda delta] # 0xf225 - - "": [T: struck ganda epsilon tidak keriting] # 0xf226 - - "": [T: struck ganda gamma] # 0xf227 - - "": [T: struck ganda eta] # 0xf228 - - "": [T: struck ganda iota] # 0xf229 - - "": [T: struck ganda xi] # 0xf22a - - "": [T: struck ganda kappa] # 0xf22b - - "": [T: struck ganda lambda] # 0xf22c - - "": [T: struck ganda mu] # 0xf22d - - "": [T: struck ganda nu] # 0xf22e - - "": [T: struck ganda epsilon] # 0xf22f - - "": [T: struck ganda pi] # 0xf230 - - "": [T: struck ganda theta] # 0xf231 - - "": [T: struck ganda rho] # 0xf232 - - "": [T: struck ganda sigma] # 0xf233 - - "": [T: struck ganda tau] # 0xf234 - - "": [T: struck ganda theta] # 0xf235 - - "": [T: struck ganda omega] # 0xf236 - - "": [T: struck ganda final sigma] # 0xf237 - - "": [T: struck ganda rho] # 0xf250 - - "": [T: struck ganda phi] # 0xf251 - - "": # 0xf260 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "𝐀": # 0x1d400 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "": # 0xf261 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "𝐁": # 0x1d401 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "": # 0xf262 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "𝐂": # 0x1d402 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "": # 0xf263 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "𝐃": # 0x1d403 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "": # 0xf264 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "𝐄": # 0x1d404 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "": # 0xf265 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "𝐅": # 0x1d405 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "": # 0xf266 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "𝐆": # 0x1d406 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "": # 0xf267 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "𝐇": # 0x1d407 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "": # 0xf268 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "𝐈": # 0x1d408 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "": # 0xf269 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "𝐉": # 0x1d409 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "": # 0xf26a - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "𝐊": # 0x1d40a - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "": # 0xf26b - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "𝐋": # 0x1d40b - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "": # 0xf26c - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "𝐌": # 0x1d40c - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "": # 0xf26d - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "𝐍": # 0x1d40d - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "": # 0xf26e - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "𝐎": # 0x1d40e - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "": # 0xf26f - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "𝐏": # 0x1d40f - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "": # 0xf270 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "𝐐": # 0x1d410 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "": # 0xf271 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "𝐑": # 0x1d411 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "": # 0xf272 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "𝐒": # 0x1d412 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "": # 0xf273 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "𝐓": # 0x1d413 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "": # 0xf274 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "𝐔": # 0x1d414 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "": # 0xf275 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "𝐕": # 0x1d415 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "": # 0xf276 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "𝐖": # 0x1d416 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "": # 0xf277 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "𝐗": # 0x1d417 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "": # 0xf278 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "𝐘": # 0x1d418 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "": # 0xf279 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "𝐙": # 0x1d419 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "": [T: bold a] # 0xf27a - - "𝐚": [T: bold a] # 0x1d41a - - "": [T: bold b] # 0xf27b - - "𝐛": [T: bold b] # 0x1d41b - - "": [T: bold c] # 0xf27c - - "𝐜": [T: bold c] # 0x1d41c - - "": [T: bold d] # 0xf27d - - "𝐝": [T: bold d] # 0x1d41d - - "": [T: bold e] # 0xf27e - - "𝐞": [T: bold e] # 0x1d41e - - "": [T: bold f] # 0xf27f - - "𝐟": [T: bold f] # 0x1d41f - - "": [T: bold g] # 0xf280 - - "𝐠": [T: bold g] # 0x1d420 - - "": [T: bold h] # 0xf281 - - "𝐡": [T: bold h] # 0x1d421 - - "": [T: bold i] # 0xf282 - - "𝐢": [T: bold i] # 0x1d422 - - "": [T: bold j] # 0xf283 - - "𝐣": [T: bold j] # 0x1d423 - - "": [T: bold k] # 0xf284 - - "𝐤": [T: bold k] # 0x1d424 - - "": [T: bold l] # 0xf285 - - "𝐥": [T: bold l] # 0x1d425 - - "": [T: bold m] # 0xf286 - - "𝐦": [T: bold m] # 0x1d426 - - "": [T: bold n] # 0xf287 - - "𝐧": [T: bold n] # 0x1d427 - - "": [T: bold o] # 0xf288 - - "𝐨": [T: bold o] # 0x1d428 - - "": [T: bold p] # 0xf289 - - "𝐩": [T: bold p] # 0x1d429 - - "": [T: bold q] # 0xf28a - - "𝐪": [T: bold q] # 0x1d42a - - "": [T: bold r] # 0xf28b - - "𝐫": [T: bold r] # 0x1d42b - - "": [T: bold s] # 0xf28c - - "𝐬": [T: bold s] # 0x1d42c - - "": [T: bold t] # 0xf28d - - "𝐭": [T: bold t] # 0x1d42d - - "": [T: bold u] # 0xf28e - - "𝐮": [T: bold u] # 0x1d42e - - "": [T: bold v] # 0xf28f - - "𝐯": [T: bold v] # 0x1d42f - - "": [T: bold w] # 0xf290 - - "𝐰": [T: bold w] # 0x1d430 - - "": [T: bold x] # 0xf291 - - "𝐱": [T: bold x] # 0x1d431 - - "": [T: bold y] # 0xf292 - - "𝐲": [T: bold y] # 0x1d432 - - "": [T: bold z] # 0xf293 - - "𝐳": [T: bold z] # 0x1d433 - - "": # 0xf294 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "𝐴": # 0x1d434 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "": # 0xf295 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "𝐵": # 0x1d435 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "": # 0xf296 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "𝐶": # 0x1d436 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "": # 0xf297 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "𝐷": # 0x1d437 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "": # 0xf298 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "𝐸": # 0x1d438 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "": # 0xf299 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "𝐹": # 0x1d439 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "": # 0xf29a - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "𝐺": # 0x1d43a - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "": # 0xf29b - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "𝐻": # 0x1d43b - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "": # 0xf29c - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "𝐼": # 0x1d43c - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "": # 0xf29d - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "𝐽": # 0x1d43d - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "": # 0xf29e - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "𝐾": # 0x1d43e - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "": # 0xf29f - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "𝐿": # 0x1d43f - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "": # 0xf2a0 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "𝑀": # 0x1d440 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "": # 0xf2a1 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "𝑁": # 0x1d441 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "": # 0xf2a2 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "𝑂": # 0x1d442 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "": # 0xf2a3 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "𝑃": # 0x1d443 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "": # 0xf2a4 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "𝑄": # 0x1d444 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "": # 0xf2a5 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "𝑅": # 0x1d445 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "": # 0xf2a6 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "𝑆": # 0x1d446 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "": # 0xf2a7 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "𝑇": # 0x1d447 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "": # 0xf2a8 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "𝑈": # 0x1d448 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "": # 0xf2a9 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "𝑉": # 0x1d449 - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "": # 0xf2aa - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "𝑊": # 0x1d44a - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "": # 0xf2ab - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "𝑋": # 0x1d44b - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "": # 0xf2ac - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "𝑌": # 0x1d44c - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "": # 0xf2ad - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "𝑍": # 0x1d44d - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "": [T: b] # 0xf2af - - "𝑏": [T: b] # 0x1d44f - - "": [T: c] # 0xf2b0 - - "𝑐": [T: c] # 0x1d450 - - "": [T: d] # 0xf2b1 - - "𝑑": [T: d] # 0x1d451 - - "": [T: e] # 0xf2b2 - - "𝑒": [T: e] # 0x1d452 - - "": [T: f] # 0xf2b3 - - "𝑓": [T: f] # 0x1d453 - - "": [T: g] # 0xf2b4 - - "𝑔": [T: g] # 0x1d454 - - "": [T: h] # 0xf2b5 - - "𝑕": [T: h] # 0x1d455 - - "": [T: i] # 0xf2b6 - - "𝑖": [T: i] # 0x1d456 - - "": [T: j] # 0xf2b7 - - "𝑗": [T: j] # 0x1d457 - - "": [T: k] # 0xf2b8 - - "𝑘": [T: k] # 0x1d458 - - "": [T: l] # 0xf2b9 - - "𝑙": [T: l] # 0x1d459 - - "": [T: m] # 0xf2ba - - "𝑚": [T: m] # 0x1d45a - - "": [T: n] # 0xf2bb - - "𝑛": [T: n] # 0x1d45b - - "": [T: o] # 0xf2bc - - "𝑜": [T: o] # 0x1d45c - - "": [T: p] # 0xf2bd - - "𝑝": [T: p] # 0x1d45d - - "": [T: q] # 0xf2be - - "𝑞": [T: q] # 0x1d45e - - "": [T: r] # 0xf2bf - - "𝑟": [T: r] # 0x1d45f - - "": [T: s] # 0xf2c0 - - "𝑠": [T: s] # 0x1d460 - - "": [T: t] # 0xf2c1 - - "𝑡": [T: t] # 0x1d461 - - "": [T: u] # 0xf2c2 - - "𝑢": [T: u] # 0x1d462 - - "": [T: v] # 0xf2c3 - - "𝑣": [T: v] # 0x1d463 - - "": [T: w] # 0xf2c4 - - "𝑤": [T: w] # 0x1d464 - - "": [T: x] # 0xf2c5 - - "𝑥": [T: x] # 0x1d465 - - "": [T: y] # 0xf2c6 - - "𝑦": [T: y] # 0x1d466 - - "": [T: z] # 0xf2c7 - - "𝑧": [T: z] # 0x1d467 - - "": # 0xf2c8 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "𝑨": # 0x1d468 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "": # 0xf2c9 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "𝑩": # 0x1d469 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "": # 0xf2ca - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "𝑪": # 0x1d46a - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "": # 0xf2cb - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "𝑫": # 0x1d46b - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "": # 0xf2cc - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "𝑬": # 0x1d46c - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "": # 0xf2cd - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "𝑭": # 0x1d46d - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "": # 0xf2ce - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "𝑮": # 0x1d46e - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "": # 0xf2cf - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "𝑯": # 0x1d46f - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "": # 0xf2d0 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "𝑰": # 0x1d470 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "": # 0xf2d1 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "𝑱": # 0x1d471 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "": # 0xf2d2 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "𝑲": # 0x1d472 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "": # 0xf2d3 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "𝑳": # 0x1d473 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "": # 0xf2d4 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "𝑴": # 0x1d474 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "": # 0xf2d5 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "𝑵": # 0x1d475 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "": # 0xf2d6 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "𝑶": # 0x1d476 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "": # 0xf2d7 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "𝑷": # 0x1d477 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "": # 0xf2d8 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "𝑸": # 0x1d478 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "": # 0xf2d9 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "𝑹": # 0x1d479 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "": # 0xf2da - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "𝑺": # 0x1d47a - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "": # 0xf2db - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "𝑻": # 0x1d47b - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "": # 0xf2dc - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "𝑼": # 0x1d47c - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "": # 0xf2dd - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "𝑽": # 0x1d47d - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "": # 0xf2de - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "𝑾": # 0x1d47e - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "": # 0xf2df - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "𝑿": # 0x1d47f - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "": # 0xf2e0 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "𝒀": # 0x1d480 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "": # 0xf2e1 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "𝒁": # 0x1d481 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "": [T: bold italic a] # 0xf2e2 - - "𝒂": [T: bold italic a] # 0x1d482 - - "": [T: bold italic b] # 0xf2e3 - - "𝒃": [T: bold italic b] # 0x1d483 - - "": [T: bold italic c] # 0xf2e4 - - "𝒄": [T: bold italic c] # 0x1d484 - - "": [T: bold italic d] # 0xf2e5 - - "𝒅": [T: bold italic d] # 0x1d485 - - "": [T: bold italic e] # 0xf2e6 - - "𝒆": [T: bold italic e] # 0x1d486 - - "": [T: bold italic f] # 0xf2e7 - - "𝒇": [T: bold italic f] # 0x1d487 - - "": [T: bold italic g] # 0xf2e8 - - "𝒈": [T: bold italic g] # 0x1d488 - - "": [T: bold italic h] # 0xf2e9 - - "𝒉": [T: bold italic h] # 0x1d489 - - "": [T: bold italic i] # 0xf2ea - - "𝒊": [T: bold italic i] # 0x1d48a - - "": [T: bold italic j] # 0xf2eb - - "𝒋": [T: bold italic j] # 0x1d48b - - "": [T: bold italic k] # 0xf2ec - - "𝒌": [T: bold italic k] # 0x1d48c - - "": [T: bold italic l] # 0xf2ed - - "𝒍": [T: bold italic l] # 0x1d48d - - "": [T: bold italic m] # 0xf2ee - - "𝒎": [T: bold italic m] # 0x1d48e - - "": [T: bold italic n] # 0xf2ef - - "𝒏": [T: bold italic n] # 0x1d48f - - "": [T: bold italic o] # 0xf2f0 - - "𝒐": [T: bold italic o] # 0x1d490 - - "": [T: bold italic p] # 0xf2f1 - - "𝒑": [T: bold italic p] # 0x1d491 - - "": [T: bold italic q] # 0xf2f2 - - "𝒒": [T: bold italic q] # 0x1d492 - - "": [T: bold italic r] # 0xf2f3 - - "𝒓": [T: bold italic r] # 0x1d493 - - "": [T: bold italic s] # 0xf2f4 - - "𝒔": [T: bold italic s] # 0x1d494 - - "": [T: bold italic t] # 0xf2f5 - - "𝒕": [T: bold italic t] # 0x1d495 - - "": [T: bold italic u] # 0xf2f6 - - "𝒖": [T: bold italic u] # 0x1d496 - - "": [T: bold italic v] # 0xf2f7 - - "𝒗": [T: bold italic v] # 0x1d497 - - "": [T: bold italic w] # 0xf2f8 - - "𝒘": [T: bold italic w] # 0x1d498 - - "": [T: bold italic x] # 0xf2f9 - - "𝒙": [T: bold italic x] # 0x1d499 - - "": [T: bold italic y] # 0xf2fa - - "𝒚": [T: bold italic y] # 0x1d49a - - "": [T: bold italic z] # 0xf2fb - - "𝒛": [T: bold italic z] # 0x1d49b - - "": # 0xf300 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "𝖠": # 0x1d5a0 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "": # 0xf301 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "𝖡": # 0x1d5a1 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "": # 0xf302 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "𝖢": # 0x1d5a2 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "": # 0xf303 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "𝖣": # 0x1d5a3 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "": # 0xf304 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "𝖤": # 0x1d5a4 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "": # 0xf305 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "𝖥": # 0x1d5a5 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "": # 0xf306 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "𝖦": # 0x1d5a6 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "": # 0xf307 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "𝖧": # 0x1d5a7 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "": # 0xf308 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "𝖨": # 0x1d5a8 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "": # 0xf309 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "𝖩": # 0x1d5a9 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "": # 0xf30a - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "𝖪": # 0x1d5aa - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "": # 0xf30b - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "𝖫": # 0x1d5ab - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "": # 0xf30c - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "𝖬": # 0x1d5ac - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "": # 0xf30d - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "𝖭": # 0x1d5ad - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "": # 0xf30e - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "𝖮": # 0x1d5ae - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "": # 0xf30f - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "𝖯": # 0x1d5af - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "": # 0xf310 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "𝖰": # 0x1d5b0 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "": # 0xf311 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "𝖱": # 0x1d5b1 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "": # 0xf312 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "𝖲": # 0x1d5b2 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "": # 0xf313 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "𝖳": # 0x1d5b3 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "": # 0xf314 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "𝖴": # 0x1d5b4 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "": # 0xf315 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "𝖵": # 0x1d5b5 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "": # 0xf316 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "𝖶": # 0x1d5b6 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "": # 0xf317 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "𝖷": # 0x1d5b7 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "": # 0xf318 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "𝖸": # 0x1d5b8 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "": # 0xf319 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "𝖹": # 0x1d5b9 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "": [T: a] # 0xf31a - - "𝖺": [T: a] # 0x1d5ba - - "": [T: b] # 0xf31b - - "𝖻": [T: b] # 0x1d5bb - - "": [T: c] # 0xf31c - - "𝖼": [T: c] # 0x1d5bc - - "": [T: d] # 0xf31d - - "𝖽": [T: d] # 0x1d5bd - - "": [T: e] # 0xf31e - - "𝖾": [T: e] # 0x1d5be - - "": [T: f] # 0xf31f - - "𝖿": [T: f] # 0x1d5bf - - "": [T: g] # 0xf320 - - "𝗀": [T: g] # 0x1d5c0 - - "": [T: h] # 0xf321 - - "𝗁": [T: h] # 0x1d5c1 - - "": [T: i] # 0xf322 - - "𝗂": [T: i] # 0x1d5c2 - - "": [T: j] # 0xf323 - - "𝗃": [T: j] # 0x1d5c3 - - "": [T: k] # 0xf324 - - "𝗄": [T: k] # 0x1d5c4 - - "": [T: l] # 0xf325 - - "𝗅": [T: l] # 0x1d5c5 - - "": [T: m] # 0xf326 - - "𝗆": [T: m] # 0x1d5c6 - - "": [T: n] # 0xf327 - - "𝗇": [T: n] # 0x1d5c7 - - "": [T: o] # 0xf328 - - "𝗈": [T: o] # 0x1d5c8 - - "": [T: p] # 0xf329 - - "𝗉": [T: p] # 0x1d5c9 - - "": [T: q] # 0xf32a - - "𝗊": [T: q] # 0x1d5ca - - "": [T: r] # 0xf32b - - "𝗋": [T: r] # 0x1d5cb - - "": [T: s] # 0xf32c - - "𝗌": [T: s] # 0x1d5cc - - "": [T: t] # 0xf32d - - "𝗍": [T: t] # 0x1d5cd - - "": [T: u] # 0xf32e - - "𝗎": [T: u] # 0x1d5ce - - "": [T: v] # 0xf32f - - "𝗏": [T: v] # 0x1d5cf - - "": [T: w] # 0xf330 - - "𝗐": [T: w] # 0x1d5d0 - - "": [T: x] # 0xf331 - - "𝗑": [T: x] # 0x1d5d1 - - "": [T: y] # 0xf332 - - "𝗒": [T: y] # 0x1d5d2 - - "": [T: z] # 0xf333 - - "𝗓": [T: z] # 0x1d5d3 - - "": # 0xf334 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "𝗔": # 0x1d5d4 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "": # 0xf335 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "𝗕": # 0x1d5d5 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "": # 0xf336 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "𝗖": # 0x1d5d6 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "": # 0xf337 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "𝗗": # 0x1d5d7 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "": # 0xf338 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "𝗘": # 0x1d5d8 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "": # 0xf339 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "𝗙": # 0x1d5d9 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "": # 0xf33a - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "𝗚": # 0x1d5da - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "": # 0xf33b - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "𝗛": # 0x1d5db - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "": # 0xf33c - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "𝗜": # 0x1d5dc - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "": # 0xf33d - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "𝗝": # 0x1d5dd - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "": # 0xf33e - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "𝗞": # 0x1d5de - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "": # 0xf33f - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "𝗟": # 0x1d5df - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "": # 0xf340 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "𝗠": # 0x1d5e0 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "": # 0xf341 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "𝗡": # 0x1d5e1 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "": # 0xf342 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "𝗢": # 0x1d5e2 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "": # 0xf343 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "𝗣": # 0x1d5e3 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "": # 0xf344 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "𝗤": # 0x1d5e4 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "": # 0xf345 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "𝗥": # 0x1d5e5 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "": # 0xf346 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "𝗦": # 0x1d5e6 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "": # 0xf347 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "𝗧": # 0x1d5e7 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "": # 0xf348 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "𝗨": # 0x1d5e8 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "": # 0xf349 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "𝗩": # 0x1d5e9 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "": # 0xf34a - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "𝗪": # 0x1d5ea - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "": # 0xf34b - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "𝗫": # 0x1d5eb - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "": # 0xf34c - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "𝗬": # 0x1d5ec - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "": # 0xf34d - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "𝗭": # 0x1d5ed - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "": [T: bold a] # 0xf34e - - "𝗮": [T: bold a] # 0x1d5ee - - "": [T: bold b] # 0xf34f - - "𝗯": [T: bold b] # 0x1d5ef - - "": [T: bold c] # 0xf350 - - "𝗰": [T: bold c] # 0x1d5f0 - - "": [T: bold d] # 0xf351 - - "𝗱": [T: bold d] # 0x1d5f1 - - "": [T: bold e] # 0xf352 - - "𝗲": [T: bold e] # 0x1d5f2 - - "": [T: bold f] # 0xf353 - - "𝗳": [T: bold f] # 0x1d5f3 - - "": [T: bold g] # 0xf354 - - "𝗴": [T: bold g] # 0x1d5f4 - - "": [T: bold h] # 0xf355 - - "𝗵": [T: bold h] # 0x1d5f5 - - "": [T: bold i] # 0xf356 - - "𝗶": [T: bold i] # 0x1d5f6 - - "": [T: bold j] # 0xf357 - - "𝗷": [T: bold j] # 0x1d5f7 - - "": [T: bold k] # 0xf358 - - "𝗸": [T: bold k] # 0x1d5f8 - - "": [T: bold l] # 0xf359 - - "𝗹": [T: bold l] # 0x1d5f9 - - "": [T: bold m] # 0xf35a - - "𝗺": [T: bold m] # 0x1d5fa - - "": [T: bold n] # 0xf35b - - "𝗻": [T: bold n] # 0x1d5fb - - "": [T: bold o] # 0xf35c - - "𝗼": [T: bold o] # 0x1d5fc - - "": [T: bold p] # 0xf35d - - "𝗽": [T: bold p] # 0x1d5fd - - "": [T: bold q] # 0xf35e - - "𝗾": [T: bold q] # 0x1d5fe - - "": [T: bold r] # 0xf35f - - "𝗿": [T: bold r] # 0x1d5ff - - "": [T: bold s] # 0xf360 - - "𝘀": [T: bold s] # 0x1d600 - - "": [T: bold t] # 0xf361 - - "𝘁": [T: bold t] # 0x1d601 - - "": [T: bold u] # 0xf362 - - "𝘂": [T: bold u] # 0x1d602 - - "": [T: bold v] # 0xf363 - - "𝘃": [T: bold v] # 0x1d603 - - "": [T: bold w] # 0xf364 - - "𝘄": [T: bold w] # 0x1d604 - - "": [T: bold x] # 0xf365 - - "𝘅": [T: bold x] # 0x1d605 - - "": [T: bold y] # 0xf366 - - "𝘆": [T: bold y] # 0x1d606 - - "": [T: bold z] # 0xf367 - - "𝘇": [T: bold z] # 0x1d607 - - "": # 0xf368 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "𝘈": # 0x1d608 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "": # 0xf369 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "𝘉": # 0x1d609 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "": # 0xf36a - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "𝘊": # 0x1d60a - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "": # 0xf36b - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "𝘋": # 0x1d60b - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "": # 0xf36c - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "𝘌": # 0x1d60c - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "": # 0xf36d - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "𝘍": # 0x1d60d - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "": # 0xf36e - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "𝘎": # 0x1d60e - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "": # 0xf36f - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "𝘏": # 0x1d60f - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "": # 0xf370 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "𝘐": # 0x1d610 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "": # 0xf371 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "𝘑": # 0x1d611 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "": # 0xf372 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "𝘒": # 0x1d612 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "": # 0xf373 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "𝘓": # 0x1d613 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "": # 0xf374 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "𝘔": # 0x1d614 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "": # 0xf375 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "𝘕": # 0x1d615 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "": # 0xf376 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "𝘖": # 0x1d616 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "": # 0xf377 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "𝘗": # 0x1d617 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "": # 0xf378 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "𝘘": # 0x1d618 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "": # 0xf379 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "𝘙": # 0x1d619 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "": # 0xf37a - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "𝘚": # 0x1d61a - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "": # 0xf37b - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "𝘛": # 0x1d61b - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "": # 0xf37c - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "𝘜": # 0x1d61c - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "": # 0xf37d - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "𝘝": # 0x1d61d - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "": # 0xf37e - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "𝘞": # 0x1d61e - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "": # 0xf37f - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "𝘟": # 0x1d61f - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "": # 0xf380 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "𝘠": # 0x1d620 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "": # 0xf381 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "𝘡": # 0x1d621 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "": [T: italic a] # 0xf382 - - "𝘢": [T: italic a] # 0x1d622 - - "": [T: italic b] # 0xf383 - - "𝘣": [T: italic b] # 0x1d623 - - "": [T: italic c] # 0xf384 - - "𝘤": [T: italic c] # 0x1d624 - - "": [T: italic d] # 0xf385 - - "𝘥": [T: italic d] # 0x1d625 - - "": [T: italic e] # 0xf386 - - "𝘦": [T: italic e] # 0x1d626 - - "": [T: italic f] # 0xf387 - - "𝘧": [T: italic f] # 0x1d627 - - "": [T: italic g] # 0xf388 - - "𝘨": [T: italic g] # 0x1d628 - - "": [T: italic h] # 0xf389 - - "𝘩": [T: italic h] # 0x1d629 - - "": [T: italic i] # 0xf38a - - "𝘪": [T: italic i] # 0x1d62a - - "": [T: italic j] # 0xf38b - - "𝘫": [T: italic j] # 0x1d62b - - "": [T: italic k] # 0xf38c - - "𝘬": [T: italic k] # 0x1d62c - - "": [T: italic l] # 0xf38d - - "𝘭": [T: italic l] # 0x1d62d - - "": [T: italic m] # 0xf38e - - "𝘮": [T: italic m] # 0x1d62e - - "": [T: italic n] # 0xf38f - - "𝘯": [T: italic n] # 0x1d62f - - "": [T: italic o] # 0xf390 - - "𝘰": [T: italic o] # 0x1d630 - - "": [T: italic p] # 0xf391 - - "𝘱": [T: italic p] # 0x1d631 - - "": [T: italic q] # 0xf392 - - "𝘲": [T: italic q] # 0x1d632 - - "": [T: italic r] # 0xf393 - - "𝘳": [T: italic r] # 0x1d633 - - "": [T: italic s] # 0xf394 - - "𝘴": [T: italic s] # 0x1d634 - - "": [T: italic t] # 0xf395 - - "𝘵": [T: italic t] # 0x1d635 - - "": [T: italic u] # 0xf396 - - "𝘶": [T: italic u] # 0x1d636 - - "": [T: italic v] # 0xf397 - - "𝘷": [T: italic v] # 0x1d637 - - "": [T: italic w] # 0xf398 - - "𝘸": [T: italic w] # 0x1d638 - - "": [T: italic x] # 0xf399 - - "𝘹": [T: italic x] # 0x1d639 - - "": [T: italic y] # 0xf39a - - "𝘺": [T: italic y] # 0x1d63a - - "": [T: italic z] # 0xf39b - - "𝘻": [T: italic z] # 0x1d63b - - "": # 0xf39c - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "𝘼": # 0x1d63c - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "": # 0xf39d - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "𝘽": # 0x1d63d - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "": # 0xf39e - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "𝘾": # 0x1d63e - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "": # 0xf39f - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "𝘿": # 0x1d63f - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "": # 0xf3a0 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "𝙀": # 0x1d640 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "": # 0xf3a1 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "𝙁": # 0x1d641 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "": # 0xf3a2 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "𝙂": # 0x1d642 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "": # 0xf3a3 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "𝙃": # 0x1d643 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "": # 0xf3a4 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "𝙄": # 0x1d644 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "": # 0xf3a5 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "𝙅": # 0x1d645 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "": # 0xf3a6 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "𝙆": # 0x1d646 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "": # 0xf3a7 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "𝙇": # 0x1d647 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "": # 0xf3a8 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "𝙈": # 0x1d648 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "": # 0xf3a9 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "𝙉": # 0x1d649 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "": # 0xf3aa - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "𝙊": # 0x1d64a - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "": # 0xf3ab - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "𝙋": # 0x1d64b - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "": # 0xf3ac - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "𝙌": # 0x1d64c - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "": # 0xf3ad - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "𝙍": # 0x1d64d - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "": # 0xf3ae - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "𝙎": # 0x1d64e - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "": # 0xf3af - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "𝙏": # 0x1d64f - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "": # 0xf3b0 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "𝙐": # 0x1d650 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "": # 0xf3b1 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "𝙑": # 0x1d651 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "": # 0xf3b2 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "𝙒": # 0x1d652 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "": # 0xf3b3 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "𝙓": # 0x1d653 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "": # 0xf3b4 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "𝙔": # 0x1d654 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "": # 0xf3b5 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "𝙕": # 0x1d655 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "": [T: bold italic a] # 0xf3b6 - - "𝙖": [T: bold italic a] # 0x1d656 - - "": [T: bold italic b] # 0xf3b7 - - "𝙗": [T: bold italic b] # 0x1d657 - - "": [T: bold italic c] # 0xf3b8 - - "𝙘": [T: bold italic c] # 0x1d658 - - "": [T: bold italic d] # 0xf3b9 - - "𝙙": [T: bold italic d] # 0x1d659 - - "": [T: bold italic e] # 0xf3ba - - "𝙚": [T: bold italic e] # 0x1d65a - - "": [T: bold italic f] # 0xf3bb - - "𝙛": [T: bold italic f] # 0x1d65b - - "": [T: bold italic g] # 0xf3bc - - "𝙜": [T: bold italic g] # 0x1d65c - - "": [T: bold italic h] # 0xf3bd - - "𝙝": [T: bold italic h] # 0x1d65d - - "": [T: bold italic i] # 0xf3be - - "𝙞": [T: bold italic i] # 0x1d65e - - "": [T: bold italic j] # 0xf3bf - - "𝙟": [T: bold italic j] # 0x1d65f - - "": [T: bold italic k] # 0xf3c0 - - "𝙠": [T: bold italic k] # 0x1d660 - - "": [T: bold italic l] # 0xf3c1 - - "𝙡": [T: bold italic l] # 0x1d661 - - "": [T: bold italic m] # 0xf3c2 - - "𝙢": [T: bold italic m] # 0x1d662 - - "": [T: bold italic n] # 0xf3c3 - - "𝙣": [T: bold italic n] # 0x1d663 - - "": [T: bold italic o] # 0xf3c4 - - "𝙤": [T: bold italic o] # 0x1d664 - - "": [T: bold italic p] # 0xf3c5 - - "𝙥": [T: bold italic p] # 0x1d665 - - "": [T: bold italic q] # 0xf3c6 - - "𝙦": [T: bold italic q] # 0x1d666 - - "": [T: bold italic r] # 0xf3c7 - - "𝙧": [T: bold italic r] # 0x1d667 - - "": [T: bold italic s] # 0xf3c8 - - "𝙨": [T: bold italic s] # 0x1d668 - - "": [T: bold italic t] # 0xf3c9 - - "𝙩": [T: bold italic t] # 0x1d669 - - "": [T: bold italic u] # 0xf3ca - - "𝙪": [T: bold italic u] # 0x1d66a - - "": [T: bold italic v] # 0xf3cb - - "𝙫": [T: bold italic v] # 0x1d66b - - "": [T: bold italic w] # 0xf3cc - - "𝙬": [T: bold italic w] # 0x1d66c - - "": [T: bold italic x] # 0xf3cd - - "𝙭": [T: bold italic x] # 0x1d66d - - "": [T: bold italic y] # 0xf3ce - - "𝙮": [T: bold italic y] # 0x1d66e - - "": [T: bold italic z] # 0xf3cf - - "𝙯": [T: bold italic z] # 0x1d66f - - "": # 0xf3d0 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "𝙰": # 0x1d670 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: a - - "": # 0xf3d1 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "𝙱": # 0x1d671 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: b - - "": # 0xf3d2 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "𝙲": # 0x1d672 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: c - - "": # 0xf3d3 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "𝙳": # 0x1d673 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: d - - "": # 0xf3d4 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "𝙴": # 0x1d674 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: e - - "": # 0xf3d5 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "𝙵": # 0x1d675 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: f - - "": # 0xf3d6 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "𝙶": # 0x1d676 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: g - - "": # 0xf3d7 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "𝙷": # 0x1d677 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: h - - "": # 0xf3d8 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "𝙸": # 0x1d678 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: i - - "": # 0xf3d9 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "𝙹": # 0x1d679 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: j - - "": # 0xf3da - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "𝙺": # 0x1d67a - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: k - - "": # 0xf3db - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "𝙻": # 0x1d67b - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: l - - "": # 0xf3dc - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "𝙼": # 0x1d67c - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: m - - "": # 0xf3dd - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "𝙽": # 0x1d67d - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: n - - "": # 0xf3de - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "𝙾": # 0x1d67e - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: o - - "": # 0xf3df - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "𝙿": # 0x1d67f - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: p - - "": # 0xf3e0 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "𝚀": # 0x1d680 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: q - - "": # 0xf3e1 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "𝚁": # 0x1d681 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: r - - "": # 0xf3e2 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "𝚂": # 0x1d682 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: s - - "": # 0xf3e3 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "𝚃": # 0x1d683 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: t - - "": # 0xf3e4 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "𝚄": # 0x1d684 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: u - - "": # 0xf3e5 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "𝚅": # 0x1d685 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: v - - "": # 0xf3e6 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "𝚆": # 0x1d686 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: w - - "": # 0xf3e7 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "𝚇": # 0x1d687 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: x - - "": # 0xf3e8 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "𝚈": # 0x1d688 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: y - - "": # 0xf3e9 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "𝚉": # 0x1d689 - - T: "" - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: z - - "": [T: a] # 0xf3ea - - "𝚊": [T: a] # 0x1d68a - - "": [T: b] # 0xf3eb - - "𝚋": [T: b] # 0x1d68b - - "": [T: c] # 0xf3ec - - "𝚌": [T: c] # 0x1d68c - - "": [T: d] # 0xf3ed - - "𝚍": [T: d] # 0x1d68d - - "": [T: e] # 0xf3ee - - "𝚎": [T: e] # 0x1d68e - - "": [T: f] # 0xf3ef - - "𝚏": [T: f] # 0x1d68f - - "": [T: g] # 0xf3f0 - - "𝚐": [T: g] # 0x1d690 - - "": [T: h] # 0xf3f1 - - "𝚑": [T: h] # 0x1d691 - - "": [T: i] # 0xf3f2 - - "𝚒": [T: i] # 0x1d692 - - "": [T: j] # 0xf3f3 - - "𝚓": [T: j] # 0x1d693 - - "": [T: k] # 0xf3f4 - - "𝚔": [T: k] # 0x1d694 - - "": [T: l] # 0xf3f5 - - "𝚕": [T: l] # 0x1d695 - - "": [T: m] # 0xf3f6 - - "𝚖": [T: m] # 0x1d696 - - "": [T: n] # 0xf3f7 - - "𝚗": [T: n] # 0x1d697 - - "": [T: o] # 0xf3f8 - - "𝚘": [T: o] # 0x1d698 - - "": [T: p] # 0xf3f9 - - "𝚙": [T: p] # 0x1d699 - - "": [T: q] # 0xf3fa - - "𝚚": [T: q] # 0x1d69a - - "": [T: r] # 0xf3fb - - "𝚛": [T: r] # 0x1d69b - - "": [T: s] # 0xf3fc - - "𝚜": [T: s] # 0x1d69c - - "": [T: t] # 0xf3fd - - "𝚝": [T: t] # 0x1d69d - - "": [T: u] # 0xf3fe - - "𝚞": [T: u] # 0x1d69e - - "": [T: v] # 0xf3ff - - "𝚟": [T: v] # 0x1d69f - - "": [T: w] # 0xf400 - - "𝚠": [T: w] # 0x1d6a0 - - "": [T: x] # 0xf401 - - "𝚡": [T: x] # 0x1d6a1 - - "": [T: y] # 0xf402 - - "𝚢": [T: y] # 0x1d6a2 - - "": [T: z] # 0xf403 - - "𝚣": [T: z] # 0x1d6a3 - - "": [T: i tanpa titik] # 0xf404 - - "𝚤": [T: i tanpa titik] # 0x1d6a4 - - "𝚥": [T: j tanpa titik] # 0x1d6a5 - - "": # 0xf408 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: alpha - - "𝚨": # 0x1d6a8 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: alpha - - "": # 0xf409 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: beta - - "𝚩": # 0x1d6a9 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: beta - - "": # 0xf40a - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: gamma - - "𝚪": # 0x1d6aa - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: gamma - - "": # 0xf40b - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: delta - - "𝚫": # 0x1d6ab - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: delta - - "": # 0xf40c - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: epsilon - - "𝚬": # 0x1d6ac - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: epsilon - - "": # 0xf40d - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: zeta - - "𝚭": # 0x1d6ad - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: zeta - - "": # 0xf40e - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: eta - - "𝚮": # 0x1d6ae - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: eta - - "": # 0xf40f - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "𝚯": # 0x1d6af - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "": # 0xf410 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: iota - - "𝚰": # 0x1d6b0 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: iota - - "": # 0xf411 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: kappa - - "𝚱": # 0x1d6b1 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: kappa - - "": # 0xf412 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: lamda - - "𝚲": # 0x1d6b2 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: lamda - - "": # 0xf413 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: mu - - "𝚳": # 0x1d6b3 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: mu - - "": # 0xf414 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: nu - - "𝚴": # 0x1d6b4 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: nu - - "": # 0xf415 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: xi - - "𝚵": # 0x1d6b5 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: xi - - "": # 0xf416 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omicron - - "𝚶": # 0x1d6b6 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omicron - - "": # 0xf417 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: pi - - "𝚷": # 0x1d6b7 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: pi - - "": # 0xf418 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: rho - - "𝚸": # 0x1d6b8 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: rho - - "": # 0xf419 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "𝚹": # 0x1d6b9 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "": # 0xf41a - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: sigma - - "𝚺": # 0x1d6ba - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: sigma - - "": # 0xf41b - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: tau - - "𝚻": # 0x1d6bb - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: tau - - "": # 0xf41c - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: upsilon - - "𝚼": # 0x1d6bc - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: upsilon - - "": # 0xf41d - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: phi - - "𝚽": # 0x1d6bd - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: phi - - "": # 0xf41e - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: chi - - "𝚾": # 0x1d6be - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: chi - - "": # 0xf41f - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: psi - - "𝚿": # 0x1d6bf - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: psi - - "": # 0xf420 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omega - - "𝛀": # 0x1d6c0 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omega - - "": [T: bold nabla] # 0xf421 - - "𝛁": [T: bold nabla] # 0x1d6c1 - - "": [T: bold alpha] # 0xf422 - - "𝛂": [T: bold alpha] # 0x1d6c2 - - "": [T: bold beta] # 0xf423 - - "𝛃": [T: bold beta] # 0x1d6c3 - - "": [T: bold gamma] # 0xf424 - - "𝛄": [T: bold gamma] # 0x1d6c4 - - "": [T: bold delta] # 0xf425 - - "𝛅": [T: bold delta] # 0x1d6c5 - - "": [T: bold epsilon] # 0xf426 - - "𝛆": [T: bold epsilon] # 0x1d6c6 - - "": [T: bold zeta] # 0xf427 - - "𝛇": [T: bold zeta] # 0x1d6c7 - - "": [T: bold eta] # 0xf428 - - "𝛈": [T: bold eta] # 0x1d6c8 - - "": [T: bold theta] # 0xf429 - - "𝛉": [T: bold theta] # 0x1d6c9 - - "": [T: bold iota] # 0xf42a - - "𝛊": [T: bold iota] # 0x1d6ca - - "": [T: bold kappa] # 0xf42b - - "𝛋": [T: bold kappa] # 0x1d6cb - - "": [T: bold lamda] # 0xf42c - - "𝛌": [T: bold lamda] # 0x1d6cc - - "": [T: bold mu] # 0xf42d - - "𝛍": [T: bold mu] # 0x1d6cd - - "": [T: bold nu] # 0xf42e - - "𝛎": [T: bold nu] # 0x1d6ce - - "": [T: bold xi] # 0xf42f - - "𝛏": [T: bold xi] # 0x1d6cf - - "": [T: bold omicron] # 0xf430 - - "𝛐": [T: bold omicron] # 0x1d6d0 - - "": [T: bold pi] # 0xf431 - - "𝛑": [T: bold pi] # 0x1d6d1 - - "": [T: bold rho] # 0xf432 - - "𝛒": [T: bold rho] # 0x1d6d2 - - "": [T: bold final sigma] # 0xf433 - - "𝛓": [T: bold final sigma] # 0x1d6d3 - - "": [T: bold sigma] # 0xf434 - - "𝛔": [T: bold sigma] # 0x1d6d4 - - "": [T: bold tau] # 0xf435 - - "𝛕": [T: bold tau] # 0x1d6d5 - - "": [T: bold upsilon] # 0xf436 - - "𝛖": [T: bold upsilon] # 0x1d6d6 - - "": [T: bold phi] # 0xf437 - - "𝛗": [T: bold phi] # 0x1d6d7 - - "": [T: bold chi] # 0xf438 - - "𝛘": [T: bold chi] # 0x1d6d8 - - "": [T: bold psi] # 0xf439 - - "𝛙": [T: bold psi] # 0x1d6d9 - - "": [T: bold omega] # 0xf43a - - "𝛚": [T: bold omega] # 0x1d6da - - "": [T: bold diferensial parsial] # 0xf43b - - "𝛛": [T: bold diferensial parsial] # 0x1d6db - - "": [T: bold epsilon] # 0xf43c - - "𝛜": [T: bold epsilon] # 0x1d6dc - - "": [T: bold theta] # 0xf43d - - "𝛝": [T: bold theta] # 0x1d6dd - - "": [T: bold kappa] # 0xf43e - - "𝛞": [T: bold kappa] # 0x1d6de - - "": [T: bold phi] # 0xf43f - - "𝛟": [T: bold phi] # 0x1d6df - - "": [T: bold rho] # 0xf440 - - "𝛠": [T: bold rho] # 0x1d6e0 - - "": [T: bold pi] # 0xf441 - - "𝛡": [T: bold pi] # 0x1d6e1 - - "": # 0xf442 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: alpha - - "𝛢": # 0x1d6e2 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: alpha - - "": # 0xf443 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: beta - - "𝛣": # 0x1d6e3 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: beta - - "": # 0xf444 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: gamma - - "𝛤": # 0x1d6e4 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: gamma - - "": # 0xf445 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: delta - - "𝛥": # 0x1d6e5 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: delta - - "": # 0xf446 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: epsilon - - "𝛦": # 0x1d6e6 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: epsilon - - "": # 0xf447 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: zeta - - "𝛧": # 0x1d6e7 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: zeta - - "": # 0xf448 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: eta - - "𝛨": # 0x1d6e8 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: eta - - "": # 0xf449 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "𝛩": # 0x1d6e9 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "": # 0xf44a - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: iota - - "𝛪": # 0x1d6ea - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: iota - - "": # 0xf44b - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: kappa - - "𝛫": # 0x1d6eb - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: kappa - - "": # 0xf44c - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: lamda - - "𝛬": # 0x1d6ec - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: lamda - - "": # 0xf44d - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: mu - - "𝛭": # 0x1d6ed - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: mu - - "": # 0xf44e - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: nu - - "𝛮": # 0x1d6ee - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: nu - - "": # 0xf44f - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: xi - - "𝛯": # 0x1d6ef - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: xi - - "": # 0xf450 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omicron - - "𝛰": # 0x1d6f0 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omicron - - "": # 0xf451 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: pi - - "𝛱": # 0x1d6f1 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: pi - - "": # 0xf452 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: rho - - "𝛲": # 0x1d6f2 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: rho - - "": # 0xf453 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "𝛳": # 0x1d6f3 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "": # 0xf454 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: sigma - - "𝛴": # 0x1d6f4 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: sigma - - "": # 0xf455 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: tau - - "𝛵": # 0x1d6f5 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: tau - - "": # 0xf456 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: upsilon - - "𝛶": # 0x1d6f6 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: upsilon - - "": # 0xf457 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: phi - - "𝛷": # 0x1d6f7 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: phi - - "": # 0xf458 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: chi - - "𝛸": # 0x1d6f8 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: chi - - "": # 0xf459 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: psi - - "𝛹": # 0x1d6f9 - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: psi - - "": # 0xf45a - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omega - - "𝛺": # 0x1d6fa - - T: italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omega - - "": [T: bold nabla] # 0xf45b - - "𝛻": [T: bold nabla] # 0x1d6fb - - "": [T: italic alpha] # 0xf45c - - "𝛼": [T: italic alpha] # 0x1d6fc - - "": [T: italic beta] # 0xf45d - - "𝛽": [T: italic beta] # 0x1d6fd - - "": [T: italic gamma] # 0xf45e - - "𝛾": [T: italic gamma] # 0x1d6fe - - "": [T: italic delta] # 0xf45f - - "𝛿": [T: italic delta] # 0x1d6ff - - "": [T: italic epsilon] # 0xf460 - - "𝜀": [T: italic epsilon] # 0x1d700 - - "": [T: italic zeta] # 0xf461 - - "𝜁": [T: italic zeta] # 0x1d701 - - "": [T: italic eta] # 0xf462 - - "𝜂": [T: italic eta] # 0x1d702 - - "": [T: italic theta] # 0xf463 - - "𝜃": [T: italic theta] # 0x1d703 - - "": [T: italic iota] # 0xf464 - - "𝜄": [T: italic iota] # 0x1d704 - - "": [T: italic kappa] # 0xf465 - - "𝜅": [T: italic kappa] # 0x1d705 - - "": [T: italic lamda] # 0xf466 - - "𝜆": [T: italic lamda] # 0x1d706 - - "": [T: italic mu] # 0xf467 - - "𝜇": [T: italic mu] # 0x1d707 - - "": [T: italic nu] # 0xf468 - - "𝜈": [T: italic nu] # 0x1d708 - - "": [T: italic xi] # 0xf469 - - "𝜉": [T: italic xi] # 0x1d709 - - "": [T: italic omicron] # 0xf46a - - "𝜊": [T: italic omicron] # 0x1d70a - - "": [T: italic pi] # 0xf46b - - "𝜋": [T: italic pi] # 0x1d70b - - "": [T: italic rho] # 0xf46c - - "𝜌": [T: italic rho] # 0x1d70c - - "": [T: italic final sigma] # 0xf46d - - "𝜍": [T: italic final sigma] # 0x1d70d - - "": [T: italic sigma] # 0xf46e - - "𝜎": [T: italic sigma] # 0x1d70e - - "": [T: italic tau] # 0xf46f - - "𝜏": [T: italic tau] # 0x1d70f - - "": [T: italic upsilon] # 0xf470 - - "𝜐": [T: italic upsilon] # 0x1d710 - - "": [T: italic phi] # 0xf471 - - "𝜑": [T: italic phi] # 0x1d711 - - "": [T: italic chi] # 0xf472 - - "𝜒": [T: italic chi] # 0x1d712 - - "": [T: italic psi] # 0xf473 - - "𝜓": [T: italic psi] # 0x1d713 - - "": [T: italic omega] # 0xf474 - - "𝜔": [T: italic omega] # 0x1d714 - - "": [T: bold diferensial parsial] # 0xf475 - - "𝜕": [T: bold diferensial parsial] # 0x1d715 - - "": [T: italic epsilon] # 0xf476 - - "𝜖": [T: italic epsilon] # 0x1d716 - - "": [T: italic theta] # 0xf477 - - "𝜗": [T: italic theta] # 0x1d717 - - "": [T: italic kappa] # 0xf478 - - "𝜘": [T: italic kappa] # 0x1d718 - - "": [T: italic phi] # 0xf479 - - "𝜙": [T: italic phi] # 0x1d719 - - "": [T: italic rho] # 0xf47a - - "𝜚": [T: italic rho] # 0x1d71a - - "": [T: italic pi] # 0xf47b - - "𝜛": [T: italic pi] # 0x1d71b - - "": # 0xf47c - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: alpha - - "𝜜": # 0x1d71c - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: alpha - - "": # 0xf47d - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: beta - - "𝜝": # 0x1d71d - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: beta - - "": # 0xf47e - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: gamma - - "𝜞": # 0x1d71e - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: gamma - - "": # 0xf47f - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: delta - - "𝜟": # 0x1d71f - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: delta - - "": # 0xf480 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: epsilon - - "𝜠": # 0x1d720 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: epsilon - - "": # 0xf481 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: zeta - - "𝜡": # 0x1d721 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: zeta - - "": # 0xf482 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: eta - - "𝜢": # 0x1d722 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: eta - - "": # 0xf483 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "𝜣": # 0x1d723 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "": # 0xf484 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: iota - - "𝜤": # 0x1d724 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: iota - - "": # 0xf485 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: kappa - - "𝜥": # 0x1d725 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: kappa - - "": # 0xf486 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: lamda - - "𝜦": # 0x1d726 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: lamda - - "": # 0xf487 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: mu - - "𝜧": # 0x1d727 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: mu - - "": # 0xf488 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: nu - - "𝜨": # 0x1d728 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: nu - - "": # 0xf489 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: xi - - "𝜩": # 0x1d729 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: xi - - "": # 0xf48a - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omicron - - "𝜪": # 0x1d72a - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omicron - - "": # 0xf48b - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: pi - - "𝜫": # 0x1d72b - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: pi - - "": # 0xf48c - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: rho - - "𝜬": # 0x1d72c - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: rho - - "": # 0xf48d - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "𝜭": # 0x1d72d - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "": # 0xf48e - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: sigma - - "𝜮": # 0x1d72e - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: sigma - - "": # 0xf48f - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: tau - - "𝜯": # 0x1d72f - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: tau - - "": # 0xf490 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: upsilon - - "𝜰": # 0x1d730 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: upsilon - - "": # 0xf491 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: phi - - "𝜱": # 0x1d731 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: phi - - "": # 0xf492 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: chi - - "𝜲": # 0x1d732 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: chi - - "": # 0xf493 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: psi - - "𝜳": # 0x1d733 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: psi - - "": # 0xf494 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omega - - "𝜴": # 0x1d734 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omega - - "": [T: bold nabla] # 0xf495 - - "𝜵": [T: bold nabla] # 0x1d735 - - "": [T: bold italic nabla] # 0xf495 - - "𝜶": [T: bold italic alpha] # 0x1d736 - - "": [T: bold italic beta] # 0xf497 - - "𝜷": [T: bold italic beta] # 0x1d737 - - "": [T: bold italic gamma] # 0xf498 - - "𝜸": [T: bold italic gamma] # 0x1d738 - - "": [T: bold italic delta] # 0xf499 - - "𝜹": [T: bold italic delta] # 0x1d739 - - "": [T: bold italic epsilon] # 0xf49a - - "𝜺": [T: bold italic epsilon] # 0x1d73a - - "": [T: bold italic zeta] # 0xf49b - - "𝜻": [T: bold italic zeta] # 0x1d73b - - "": [T: bold italic eta] # 0xf49c - - "𝜼": [T: bold italic eta] # 0x1d73c - - "": [T: bold italic theta] # 0xf49d - - "𝜽": [T: bold italic theta] # 0x1d73d - - "": [T: bold italic iota] # 0xf49e - - "𝜾": [T: bold italic iota] # 0x1d73e - - "": [T: bold italic kappa] # 0xf49f - - "𝜿": [T: bold italic kappa] # 0x1d73f - - "": [T: bold italic lamda] # 0xf4a0 - - "𝝀": [T: bold italic lamda] # 0x1d740 - - "": [T: bold italic mu] # 0xf4a1 - - "𝝁": [T: bold italic mu] # 0x1d741 - - "": [T: bold italic nu] # 0xf4a2 - - "𝝂": [T: bold italic nu] # 0x1d742 - - "": [T: bold italic xi] # 0xf4a3 - - "𝝃": [T: bold italic xi] # 0x1d743 - - "": [T: bold italic omicron] # 0xf4a4 - - "𝝄": [T: bold italic omicron] # 0x1d744 - - "": [T: bold italic pi] # 0xf4a5 - - "𝝅": [T: bold italic pi] # 0x1d745 - - "": [T: bold italic rho] # 0xf4a6 - - "𝝆": [T: bold italic rho] # 0x1d746 - - "": [T: bold italic final sigma] # 0xf4a7 - - "𝝇": [T: bold italic final sigma] # 0x1d747 - - "": [T: bold italic sigma] # 0xf4a8 - - "𝝈": [T: bold italic sigma] # 0x1d748 - - "": [T: bold italic tau] # 0xf4a9 - - "𝝉": [T: bold italic tau] # 0x1d749 - - "": [T: bold italic upsilon] # 0xf4aa - - "𝝊": [T: bold italic upsilon] # 0x1d74a - - "": [T: bold italic phi] # 0xf4ab - - "𝝋": [T: bold italic phi] # 0x1d74b - - "": [T: bold italic chi] # 0xf4ac - - "𝝌": [T: bold italic chi] # 0x1d74c - - "": [T: bold italic psi] # 0xf4ad - - "𝝍": [T: bold italic psi] # 0x1d74d - - "": [T: bold italic omega] # 0xf4ae - - "𝝎": [T: bold italic omega] # 0x1d74e - - "": [T: bold diferensial parsial] # 0xf4af - - "𝝏": [T: bold diferensial parsial] # 0x1d74f - - "": [T: bold italic epsilon] # 0xf4b0 - - "𝝐": [T: bold italic epsilon] # 0x1d750 - - "": [T: bold italic theta] # 0xf4b1 - - "𝝑": [T: bold italic theta] # 0x1d751 - - "": [T: bold italic kappa] # 0xf4b2 - - "𝝒": [T: bold italic kappa] # 0x1d752 - - "": [T: bold italic phi] # 0xf4b3 - - "𝝓": [T: bold italic phi] # 0x1d753 - - "": [T: bold italic rho] # 0xf4b4 - - "𝝔": [T: bold italic rho] # 0x1d754 - - "": [T: bold italic pi] # 0xf4b5 - - "𝝕": [T: bold italic pi] # 0x1d755 - - "": # 0xf4b6 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: alpha - - "𝝖": # 0x1d756 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: alpha - - "": # 0xf4b7 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: beta - - "𝝗": # 0x1d757 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: beta - - "": # 0xf4b8 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: gamma - - "𝝘": # 0x1d758 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: gamma - - "": # 0xf4b9 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: delta - - "𝝙": # 0x1d759 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: delta - - "": # 0xf4ba - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: epsilon - - "𝝚": # 0x1d75a - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: epsilon - - "": # 0xf4bb - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: zeta - - "𝝛": # 0x1d75b - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: zeta - - "": # 0xf4bc - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: eta - - "𝝜": # 0x1d75c - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: eta - - "": # 0xf4bd - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "𝝝": # 0x1d75d - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "": # 0xf4be - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: iota - - "𝝞": # 0x1d75e - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: iota - - "": # 0xf4bf - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: kappa - - "𝝟": # 0x1d75f - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: kappa - - "": # 0xf4c0 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: lamda - - "𝝠": # 0x1d760 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: lamda - - "": # 0xf4c1 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: mu - - "𝝡": # 0x1d761 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: mu - - "": # 0xf4c2 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: nu - - "𝝢": # 0x1d762 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: nu - - "": # 0xf4c3 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: xi - - "𝝣": # 0x1d763 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: xi - - "": # 0xf4c4 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omicron - - "𝝤": # 0x1d764 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omicron - - "": # 0xf4c5 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: pi - - "𝝥": # 0x1d765 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: pi - - "": # 0xf4c6 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: rho - - "𝝦": # 0x1d766 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: rho - - "": # 0xf4c7 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "𝝧": # 0x1d767 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "": # 0xf4c8 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: sigma - - "𝝨": # 0x1d768 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: sigma - - "": # 0xf4c9 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: tau - - "𝝩": # 0x1d769 - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: tau - - "": # 0xf4ca - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: upsilon - - "𝝪": # 0x1d76a - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: upsilon - - "": # 0xf4cb - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: phi - - "𝝫": # 0x1d76b - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: phi - - "": # 0xf4cc - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: chi - - "𝝬": # 0x1d76c - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: chi - - "": # 0xf4cd - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: psi - - "𝝭": # 0x1d76d - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: psi - - "": # 0xf4ce - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omega - - "𝝮": # 0x1d76e - - T: bold - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omega - - "": [T: bold nabla] # 0xf4cf - - "𝝯": [T: bold nabla] # 0x1d76f - - "": [T: bold alpha] # 0xf4d0 - - "𝝰": [T: bold alpha] # 0x1d770 - - "": [T: bold beta] # 0xf4d1 - - "𝝱": [T: bold beta] # 0x1d771 - - "": [T: bold gamma] # 0xf4d2 - - "𝝲": [T: bold gamma] # 0x1d772 - - "": [T: bold delta] # 0xf4d3 - - "𝝳": [T: bold delta] # 0x1d773 - - "": [T: bold epsilon] # 0xf4d4 - - "𝝴": [T: bold epsilon] # 0x1d774 - - "": [T: bold zeta] # 0xf4d5 - - "𝝵": [T: bold zeta] # 0x1d775 - - "": [T: bold eta] # 0xf4d6 - - "𝝶": [T: bold eta] # 0x1d776 - - "": [T: bold theta] # 0xf4d7 - - "𝝷": [T: bold theta] # 0x1d777 - - "": [T: bold iota] # 0xf4d8 - - "𝝸": [T: bold iota] # 0x1d778 - - "": [T: bold kappa] # 0xf4d9 - - "𝝹": [T: bold kappa] # 0x1d779 - - "": [T: bold lamda] # 0xf4da - - "𝝺": [T: bold lamda] # 0x1d77a - - "": [T: bold mu] # 0xf4db - - "𝝻": [T: bold mu] # 0x1d77b - - "": [T: bold nu] # 0xf4dc - - "𝝼": [T: bold nu] # 0x1d77c - - "": [T: bold xi] # 0xf4dd - - "𝝽": [T: bold xi] # 0x1d77d - - "": [T: bold omicron] # 0xf4de - - "𝝾": [T: bold omicron] # 0x1d77e - - "": [T: bold pi] # 0xf4df - - "𝝿": [T: bold pi] # 0x1d77f - - "": [T: bold rho] # 0xf4e0 - - "𝞀": [T: bold rho] # 0x1d780 - - "": [T: bold final sigma] # 0xf4e1 - - "𝞁": [T: bold final sigma] # 0x1d781 - - "": [T: bold sigma] # 0xf4e2 - - "𝞂": [T: bold sigma] # 0x1d782 - - "": [T: bold tau] # 0xf4e3 - - "𝞃": [T: bold tau] # 0x1d783 - - "": [T: bold upsilon] # 0xf4e4 - - "𝞄": [T: bold upsilon] # 0x1d784 - - "": [T: bold phi] # 0xf4e5 - - "𝞅": [T: bold phi] # 0x1d785 - - "": [T: bold chi] # 0xf4e6 - - "𝞆": [T: bold chi] # 0x1d786 - - "": [T: bold psi] # 0xf4e7 - - "𝞇": [T: bold psi] # 0x1d787 - - "": [T: bold omega] # 0xf4e8 - - "𝞈": [T: bold omega] # 0x1d788 - - "": [T: bold diferensial parsial] # 0xf4e9 - - "𝞉": [T: bold diferensial parsial] # 0x1d789 - - "": [T: bold epsilon] # 0xf4ea - - "𝞊": [T: bold epsilon] # 0x1d78a - - "": [T: bold theta] # 0xf4eb - - "𝞋": [T: bold theta] # 0x1d78b - - "": [T: bold kappa] # 0xf4ec - - "𝞌": [T: bold kappa] # 0x1d78c - - "": [T: bold phi] # 0xf4ed - - "𝞍": [T: bold phi] # 0x1d78d - - "": [T: bold rho] # 0xf4ee - - "𝞎": [T: bold rho] # 0x1d78e - - "": [T: bold pi] # 0xf4ef - - "𝞏": [T: bold pi] # 0x1d78f - - "": # 0xf4f0 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: alpha - - "𝞐": # 0x1d790 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: alpha - - "": # 0xf4f1 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: beta - - "𝞑": # 0x1d791 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: beta - - "": # 0xf4f2 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: gamma - - "𝞒": # 0x1d792 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: gamma - - "": # 0xf4f3 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: delta - - "𝞓": # 0x1d793 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: delta - - "": # 0xf4f4 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: epsilon - - "𝞔": # 0x1d794 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: epsilon - - "": # 0xf4f5 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: zeta - - "𝞕": # 0x1d795 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: zeta - - "": # 0xf4f6 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: eta - - "𝞖": # 0x1d796 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: eta - - "": # 0xf4f7 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "𝞗": # 0x1d797 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "": # 0xf4f8 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: iota - - "𝞘": # 0x1d798 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: iota - - "": # 0xf4f9 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: kappa - - "𝞙": # 0x1d799 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: kappa - - "": # 0xf4fa - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: lamda - - "𝞚": # 0x1d79a - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: lamda - - "": # 0xf4fb - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: mu - - "𝞛": # 0x1d79b - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: mu - - "": # 0xf4fc - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: nu - - "𝞜": # 0x1d79c - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: nu - - "": # 0xf4fd - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: xi - - "𝞝": # 0x1d79d - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: xi - - "": # 0xf4fe - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omicron - - "𝞞": # 0x1d79e - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omicron - - "": # 0xf4ff - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: pi - - "𝞟": # 0x1d79f - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: pi - - "": # 0xf500 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: rho - - "𝞠": # 0x1d7a0 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: rho - - "": # 0xf501 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "𝞡": # 0x1d7a1 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: theta - - "": # 0xf502 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: sigma - - "𝞢": # 0x1d7a2 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: sigma - - "": # 0xf503 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: tau - - "𝞣": # 0x1d7a3 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: tau - - "": # 0xf504 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: upsilon - - "𝞤": # 0x1d7a4 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: upsilon - - "": # 0xf505 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: phi - - "𝞥": # 0x1d7a5 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: phi - - "": # 0xf506 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: chi - - "𝞦": # 0x1d7a6 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: chi - - "": # 0xf507 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: psi - - "𝞧": # 0x1d7a7 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: psi - - "": # 0xf508 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omega - - "𝞨": # 0x1d7a8 - - T: bold italic - - test: - if: "($Blind or $Verbosity!='Terse')" - then: [T: huruf besar] - - T: omega - - "": [T: bold nabla] # 0xf509 - - "𝞩": [T: bold nabla] # 0x1d7a9 - - "": [T: bold italic alpha] # 0xf50a - - "𝞪": [T: bold italic alpha] # 0x1d7aa - - "": [T: bold italic beta] # 0xf50b - - "𝞫": [T: bold italic beta] # 0x1d7ab - - "": [T: bold italic gamma] # 0xf50c - - "𝞬": [T: bold italic gamma] # 0x1d7ac - - "": [T: bold italic delta] # 0xf50d - - "𝞭": [T: bold italic delta] # 0x1d7ad - - "": [T: bold italic epsilon] # 0xf50e - - "𝞮": [T: bold italic epsilon] # 0x1d7ae - - "": [T: bold italic zeta] # 0xf50f - - "𝞯": [T: bold italic zeta] # 0x1d7af - - "": [T: bold italic eta] # 0xf510 - - "𝞰": [T: bold italic eta] # 0x1d7b0 - - "": [T: bold italic theta] # 0xf511 - - "𝞱": [T: bold italic theta] # 0x1d7b1 - - "": [T: bold italic iota] # 0xf512 - - "𝞲": [T: bold italic iota] # 0x1d7b2 - - "": [T: bold italic kappa] # 0xf513 - - "𝞳": [T: bold italic kappa] # 0x1d7b3 - - "": [T: bold italic lamda] # 0xf514 - - "𝞴": [T: bold italic lamda] # 0x1d7b4 - - "": [T: bold italic mu] # 0xf515 - - "𝞵": [T: bold italic mu] # 0x1d7b5 - - "": [T: bold italic nu] # 0xf516 - - "𝞶": [T: bold italic nu] # 0x1d7b6 - - "": [T: bold italic xi] # 0xf517 - - "𝞷": [T: bold italic xi] # 0x1d7b7 - - "": [T: bold italic omicron] # 0xf518 - - "𝞸": [T: bold italic omicron] # 0x1d7b8 - - "": [T: bold italic pi] # 0xf519 - - "𝞹": [T: bold italic pi] # 0x1d7b9 - - "": [T: bold italic rho] # 0xf51a - - "𝞺": [T: bold italic rho] # 0x1d7ba - - "": [T: bold italic final sigma] # 0xf51b - - "𝞻": [T: bold italic final sigma] # 0x1d7bb - - "": [T: bold italic sigma] # 0xf51c - - "𝞼": [T: bold italic sigma] # 0x1d7bc - - "": [T: bold italic tau] # 0xf51d - - "𝞽": [T: bold italic tau] # 0x1d7bd - - "": [T: bold italic upsilon] # 0xf51e - - "𝞾": [T: bold italic upsilon] # 0x1d7be - - "": [T: bold italic phi] # 0xf51f - - "𝞿": [T: bold italic phi] # 0x1d7bf - - "": [T: bold italic chi] # 0xf520 + +# 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: "fraktur" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝔄𝔅𝔆𝔇𝔈𝔉𝔊𝔋𝔌𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔕𝔖𝔗𝔘𝔙𝔚𝔛𝔜', 'abcdefghijklmnopqrstuvwxy')" + + - "-": # 0xf000 - 0xf018 + - T: "fraktur" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxy')" + + - "𝔞-𝔷": # 0x1d51e - 0x1d537 + - T: "fraktur" + - spell: "translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz')" + - "-": # 0xf01a - 0xf033 + - T: "fraktur" + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝕬-𝖅": # 0x1D56C - 0x1D585 + - T: "fraktur" + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf040 - 0xf059 + - T: "fraktur" + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝖆-𝖟": # 0x1d586 - 0x1d59f + - T: "fraktur" + - T: "bold" + - spell: "translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz')" + - "-": # 0xf05a - 0xf073 + - T: "fraktur" + - T: "bold" + - 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: "struck ganda" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝔸𝔹𝔺𝔻𝔼𝔽𝔾𝔿𝕀𝕁𝕂𝕃𝕄𝕅𝕆𝕇𝕈𝕉𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf080 - 0xf098 + - T: "struck ganda" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝕒-𝕫": # 0x1d552 - 0x1d56b + - T: "struck ganda" + - spell: "translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz')" + - "-": # 0xf09a - 0xf0b3 + - T: "struck ganda" + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - "𝒜-𝒵": # 0x1d49c - 0x1d4b5 + - T: "aksara" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝒜𝒝𝒞𝒟𝒠𝒡𝒢𝒣𝒤𝒥𝒦𝒧𝒨𝒩𝒪𝒫𝒬𝒭𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf100 - 0xf119 + - T: "aksara" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝒶-𝓏": # 0x1d4b6 - 0x1d4cf + - T: "aksara" + - spell: "translate('.', '𝒶𝒷𝒸𝒹𝒺𝒻𝒼𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝓄𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏', 'abcdefghijklmnopqrstuvwxyz')" + - "-": # 0xf11a - 0xf133 + - T: "aksara" + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + # bold script chars in math alphabetic block and also MathType private use area + - "𝓐-𝓩": # 0x1d4d0 - 0x1d4e9 + - T: "aksara" + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf140 - 0xf159 + - T: "aksara" + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝓪-𝔃": # 0x1d4ea - 0x1d503 + - T: "aksara" + - T: "bold" + - spell: "translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz')" + - "-": # 0xf15a - 0xf173 + - T: "aksara" + - T: "bold" + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf180 - 0xf199 + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - 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: "cap"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + replace: [T: "ligature 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: "cap"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + replace: [T: "sharp 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: "cap"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + replace: [T: "o with stroke"] + + # MathType only has a few of the cap Greek letters in PUA + - "": # 0xf201 - 0xf209 + - T: "struck ganda" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'δψλπσθγωυ')" + + - "-": # 0xf220 - 0xf236 + - T: "struck ganda" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρ΢στυφχψω')" + + - "": [T: "struck ganda final sigma"] # 0xf237 + - "": [T: "struck ganda rho"] # 0xf250 + - "": [T: "struck ganda phi"] # 0xf251 + - "𝐀-𝐙": # 0x1d400 - 0x1d419 + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf260 - 0xf279 + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝐚-𝐳": # 0x1d41a - 0x1d433 + - T: "bold" + - spell: "translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf27a - 0xf293 + - T: "bold" + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝐴-𝑍": # 0x1d434 - 0x1d44d + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf294 - 0xf2ad + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝑎-𝑧": # 0x1d44e - 0x1d467 + - spell: "translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔𝑕𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf2ae - 0xf2c7 + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝑨-𝒁": # 0x1d468 - 0x1d481 + # - T: "bold italic" + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf2c8 - 0xf2e1 + # - T: "bold italic" + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝒂-𝒛": # 0x1d482 - 0x1d49b + # - T: "bold italic" + - T: "bold" + - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf2e2 - 0xf2fb + # - T: "bold italic" + - T: "bold" + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf300 - 0xf319 + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝖺-𝗓": # 0x1d5ba - 0x1d5d3 + - spell: "translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf31a - 0xf333 + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝗔-𝗭": # 0x1d5d4 - 0x1d5ed + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf334 - 0xf34d + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝗮-𝘇": # 0x1d5ee - 0x1d607 + - T: "bold" + - spell: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf34e - 0xf367 + - T: "bold" + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - "𝘈-𝘡": # 0x1d608 - 0x1d621 + # - T: "italic" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'abcdefghijklmnopqrstuvwxyz')" + - "-": # 0xf368 - 0xf381 + # - T: "italic" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝘢-𝘻": # 0x1d622 - 0x1d63b + # - T: "italic" + - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf382 - 0xf39b + # - T: "italic" + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝘼-𝙕": # 0x1d63c - 0x1d655 + # - T: "bold italic" + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf39c - 0xf3b5 + # - T: "bold italic" + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝙖-𝙯": # 0x1d656 - 0x1d66f + # - T: "bold italic" + - T: "bold" + - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf3b6 - 0xf3cf + # - T: "bold italic" + - T: "bold" + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝙰-𝚉": # 0x1d670 - 0x1d689 + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf3d0 - 0xf3e9 + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝚊-𝚣": # 0x1d68a - 0x1d6a3 + - spell: "translate('.', '𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf3ea - 0xf403 + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "": [T: "dotless i"] # 0xf404 + - "𝚤": [T: "dotless i"] # 0x1d6a4 + - "𝚥": [T: "dotless j"] # 0x1d6a5 + + - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'αβγδεζηθικλμνξοπρ΢στυφχψω')" + + - "-": # 0xf408 - 0xf420 + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρ΢στυφχψω')" + + - "𝛂-𝛚": # 0x1d6c2 - 0x1d6da + - T: "bold" + - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒𝛓𝛔𝛕𝛖𝛗𝛘𝛙𝛚', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "-": # 0xf422 - 0xf43a + - T: "bold" + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "": [T: "bold nabla"] # 0xf421 + - "𝛁": [T: "bold nabla"] # 0x1d6c1 + + - "𝛛𝛜𝛝𝛞𝛟𝛠𝛡": # 0x1D6DB - 0x1D6E1 + - T: "bold" + - spell: "translate('.', '𝛛𝛜𝛝𝛞𝛟𝛠𝛡', '∂εθκφρπ')" + + - "": # 0xF43C - 0xF441 + - T: "bold" + - spell: "translate('.', '', '∂εθκφρπ')" + + - "𝛢-𝛺": # 0x1d6e2 - 0x1d6fa + # - T: "italic" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹𝛺', 'αβγδεζηθικλμνξοπρ΢στυφχψω')" + + - "-": # 0xf442 - 0xf45a + # - T: "italic" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρ΢στυφχψω')" + + - "𝛼-𝜔": # 0x1d6fc - 0x1d714 + # - T: "italic" + - spell: "translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌𝜍𝜎𝜏𝜐𝜑𝜒𝜓𝜔', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "-": # 0xf45c - 0xf474 + # - T: "italic" + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "": [T: "italic nabla"] # 0xf45b + - "𝛻": [T: "italic nabla"] # 0x1d6fb + + - "𝜕𝜖𝜗𝜘𝜙𝜚𝜛": # 0x1d715 - 0x1d71b + # - T: "italic" + - spell: "translate('.', '𝜕𝜖𝜗𝜘𝜙𝜚𝜛', '∂εθκφρπ')" + + - "": # 0xf475 - 0xf47b + # - T: "italic" + - spell: "translate('.', '', '∂εθκφρπ')" + + - "𝜜-𝜴": # 0x1d71c - 0x1d734 + # - T: "bold italic" + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳𝜴', 'αβγδεζηθικλμνξοπρ΢στυφχψω')" + + - "-": # 0xf47c - 0xf494 + # - T: "bold italic" + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρ΢στυφχψω')" + + - "𝜶-𝝎": # 0x1d736 - 0x1d74e + # - T: "bold italic" + - T: "bold" + - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆𝝇𝝈𝝉𝝊𝝋𝝌𝝍𝝎', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "-": # 0xf496 - 0xf4ae + # - T: "bold italic" + - T: "bold" + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "𝝏𝝐𝝑𝝒𝝓𝝔𝝕": # 0x1d74f - 0x1d755 + # - T: "bold italic" + - T: "bold" + - spell: "translate('.', '𝝏𝝐𝝑𝝒𝝓𝝔𝝕', '∂εθκφρπ')" + + - "": # 0xf422 - 0xf43a + # - T: "bold italic" + - T: "bold" + - spell: "translate('.', '', '∂εθκφρπ')" + + - "𝜵": [T: "bold italic nabla"] # 0x1d735 + - "": [T: "bold italic nabla"] # 0xf495 + + - "𝝖-𝝮": # 0x1d756 - 0x1d76e + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭𝝮', 'αβγδεζηθικλμνξοπρ΢στυφχψω')" + - "-": # 0xf4b6 - 0xf4ce + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρ΢στυφχψω')" + + - "𝝰-𝞈": # 0x1d770 - 0x1d788 + - T: "bold" + - spell: "translate('.', '𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿𝞀𝞁𝞂𝞃𝞄𝞅𝞆𝞇𝞈', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "-": # 0xf4d0 - 0xf4e8 + - T: "bold" + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "𝞉𝞊𝞋𝞌𝞍𝞎𝞏": # 0x1d789 - 0x1d78f + - T: "bold" + - spell: "translate('.', '𝞉𝞊𝞋𝞌𝞍𝞎𝞏', '∂εθκφρπ')" + + - "": # 0xf4e9 - 0xf4ef + - T: "bold" + - spell: "translate('.', '', '∂εθκφρπ')" + + - "": [T: "bold nabla"] # 0xf4cf + - "𝝯": [T: "bold nabla"] # 0x1d76f + + - "𝞐-𝞨": # 0x1d790 - 0x1d7a8 + # - T: "bold italic" + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'αβγδεζηθικλμνξοπρ΢στυφχψω')" + + - "-": # 0xf4f0 - 0xf508 + # - T: "bold italic" + - T: "bold" + - test: + if: "($Blind or $Verbosity!='Terse')" + then: [T: huruf besar] + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρ΢στυφχψω')" + + - "𝞪-𝟂": # 0x1d7aa - 0x1d7c2 + # - T: "bold italic" + - T: "bold" + - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺𝞻𝞼𝞽𝞾𝞿𝟀𝟁𝟂', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "-": # 0xf50a - 0xf522 + # - T: "bold italic" + - T: "bold" + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "𝟃𝟄𝟅𝟆𝟇𝟈𝟉": # 0x1d7c3 - 0x1d7c9 + # - T: "bold italic" + - T: "bold" + - spell: "translate('.', '𝟃𝟄𝟅𝟆𝟇𝟈𝟉', '∂εθκφρπ')" + + - "": # 0xf523 - 0xf529 + # - T: "bold italic" + - T: "bold" + - spell: "translate('.', '', '∂εθκφρπ')" + + - "": [T: "bold nabla"] # 0xf509 + - "𝞩": [T: "bold nabla"] # 0x1d7a9 + + - "": [T: struck ganda nol] # 0xf0c0 + - "𝟘": [T: struck ganda nol] # 0x1d7d8 + - "": [T: struck ganda satu] # 0xf0c1 + - "𝟙": [T: struck ganda satu] # 0x1d7d9 + - "": [T: struck ganda dua] # 0xf0c2 + - "𝟚": [T: struck ganda dua] # 0x1d7da + - "": [T: struck ganda tiga] # 0xf0c3 + - "𝟛": [T: struck ganda tiga] # 0x1d7db + - "": [T: struck ganda empat] # 0xf0c4 + - "𝟜": [T: struck ganda empat] # 0x1d7dc + - "": [T: struck ganda lima] # 0xf0c5 + - "𝟝": [T: struck ganda lima] # 0x1d7dd + - "": [T: struck ganda enam] # 0xf0c6 + - "𝟞": [T: struck ganda enam] # 0x1d7de + - "": [T: struck ganda tujuh] # 0xf0c7 + - "𝟟": [T: struck ganda tujuh] # 0x1d7df + - "": [T: struck ganda delapan] # 0xf0c8 + - "𝟠": [T: struck ganda delapan] # 0x1d7e0 + - "": [T: struck ganda sembilan] # 0xf0c9 + - "𝟡": [T: struck ganda sembilan] # 0x1d7e1 + - "": [T: struck ganda nabla] # 0xf0ca + - "": [T: struck ganda konstanta euler] # 0xf0cb - "𝟀": [T: bold italic chi] # 0x1d7c0 - - "": [T: bold italic psi] # 0xf521 - "𝟁": [T: bold italic psi] # 0x1d7c1 - - "": [T: bold italic omega] # 0xf522 - "𝟂": [T: bold italic omega] # 0x1d7c2 - - "": [T: bold diferensial parsial] # 0xf523 - "𝟃": [T: bold diferensial parsial] # 0x1d7c3 - - "": [T: bold italic epsilon] # 0xf524 - "𝟄": [T: bold italic epsilon] # 0x1d7c4 - - "": [T: bold italic theta] # 0xf525 - "𝟅": [T: bold italic theta] # 0x1d7c5 - - "": [T: bold italic kappa] # 0xf526 - "𝟆": [T: bold italic kappa] # 0x1d7c6 - - "": [T: bold italic phi] # 0xf527 - "𝟇": [T: bold italic phi] # 0x1d7c7 - - "": [T: bold italic rho] # 0xf528 - "𝟈": [T: bold italic rho] # 0x1d7c8 - - "": [T: bold italic pi] # 0xf529 - "𝟉": [T: bold italic pi] # 0x1d7c9 - "": [T: bold nol] # 0xf52e - "𝟎": [T: bold nol] # 0x1d7ce @@ -10390,6 +4505,7 @@ - "𝟾": [T: delapan] # 0x1d7fe - "": [T: sembilan] # 0xf55f - "𝟿": [T: sembilan] # 0x1d7ff + - "": [T: karakter tidak dikenal] # 0xf700 - "": [T: segitiga kanan bawah dan kiri bawah] # 0xf726 - "": [T: pemanjang ellipsis horizontal] # 0xf72d diff --git a/src/braille.rs b/src/braille.rs index 1ced5bc8b..929bcf791 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -645,7 +645,7 @@ fn nemeth_cleanup(_pref_manager: Ref, raw_braille: String) -> "B" => "⠸", // bold "𝔹" => "⠠⠸", // blackboard "T" => "⠈", // script - "I" => "⠨", // italic (mapped to be the same a blackboard) + "I" => "⠨", // italic -- used for digits (not math letters) "R" => "", // roman "E" => "⠰", // English "D" => "⠸", // German (Deutsche) @@ -889,7 +889,7 @@ static UEB_INDICATOR_REPLACEMENTS: phf::Map<&str, &str> = phf_map! { "B" => "⠘", // bold "𝔹" => "XXX", // blackboard -- from prefs "T" => "⠈", // script - "I" => "⠨", // italic + "I" => "⠨", // italic -- used for digits (not math letters) "R" => "", // roman // "E" => "⠰", // English "1" => "⠰", // Grade 1 symbol @@ -2047,8 +2047,6 @@ static VIETNAM_INDICATOR_REPLACEMENTS: phf::Map<&str, &str> = phf_map! { "B" => "⠘", // bold "𝔹" => "XXX", // blackboard -- from prefs "T" => "⠈", // script - "I" => "⠨", // italic - "R" => "", // roman // "E" => "⠰", // English "1" => "⠠", // Grade 1 symbol "L" => "", // Letter left in to assist in locating letters @@ -2137,7 +2135,7 @@ static CMU_INDICATOR_REPLACEMENTS: phf::Map<&str, &str> = phf_map! { "B" => "⠔", // bold "𝔹" => "⠬", // blackboard -- from prefs // "T" => "⠈", // script - "I" => "⠔", // italic -- same as bold + "I" => "⠔", // italic -- used for digits (not math letters); same cell as bold in CMU // "R" => "", // roman // "E" => "⠰", // English "1" => "⠐", // Grade 1 symbol -- used here for a-j after number @@ -2230,7 +2228,7 @@ static SWEDISH_INDICATOR_REPLACEMENTS: phf::Map<&str, &str> = phf_map! { "B" => "⠨", // bold "𝔹" => "XXX", // blackboard -- from prefs "T" => "⠈", // script - "I" => "⠨", // italic + "I" => "⠨", // italic -- used for digits (not math letters) "R" => "", // roman "1" => "⠱", // Grade 1 symbol (used for number followed by a letter) "L" => "", // Letter left in to assist in locating letters @@ -2269,7 +2267,7 @@ static FINNISH_INDICATOR_REPLACEMENTS: phf::Map<&str, &str> = phf_map! { "B" => "⠨", // bold "𝔹" => "XXX", // blackboard -- from prefs "T" => "⠈", // script - "I" => "⠨", // italic + "I" => "⠨", // italic -- used for digits (not math letters) "R" => "", // roman "E" => "⠰", // English "1" => "⠀", // Grade 1 symbol (used for number followed by a letter) @@ -2719,21 +2717,34 @@ impl BrailleChars { } } + /// Italic typeform is for digits (and numeric punctuation like '.'), not math letters. + fn uses_italic_typeform(node_name: &str, text: &str) -> bool { + if node_name == "mn" { + return true; + } + !text.is_empty() && text.chars().all(|ch| ch.is_ascii_digit() || matches!(ch, '.' | ',' | '−' | '-' | '+')) + } + fn get_braille_nemeth_chars(node: Element, text_range: Option>) -> Result { // To greatly simplify typeface/language generation, the chars have unique ASCII chars for them: // Typeface: S: sans-serif, B: bold, 𝔹: blackboard, T: script, I: italic, R: Roman // Language: E: English, D: German, G: Greek, V: Greek variants, H: Hebrew, U: Russian // Indicators: C: capital, L: letter, N: number, P: punctuation, M: multipurpose + // Italic typeform is used for digits; math letters do not get italic. static PICK_APART_CHAR: LazyLock = LazyLock::new(|| { Regex::new(r"(?P[SB𝔹TIR]*)(?P[EDGVHU]?)(?PC?)(?PL?)(?P[N]?)(?P.)").unwrap() }); let raw_math_variant = node.attribute_value("mathvariant"); let math_variant = raw_math_variant.as_deref(); + let text = BrailleChars::substring(as_str!(as_text(node)), &text_range); + let node_name = name(node); + let italic_ok = BrailleChars::uses_italic_typeform(node_name, &text); let attr_typeface = match math_variant { None => "R", Some(variant) => match variant { "bold" => "B", - "italic" => "I", + "italic" => if italic_ok {"I"} else {"R"}, + "bold-italic" => if italic_ok {"BI"} else {"B"}, "double-struck" => "𝔹", "script" => "T", "fraktur" => "D", @@ -2741,7 +2752,6 @@ impl BrailleChars { _ => "R", // normal and unknown }, }; - let text = BrailleChars::substring(as_str!(as_text(node)), &text_range); let braille_chars = braille_replace_chars(&text, node)?; // debug!("Nemeth chars: text='{}', braille_chars='{}'", &text, &braille_chars); @@ -2750,7 +2760,6 @@ impl BrailleChars { // also true (sort of) for capitalization -- if all caps, use double cap in front (assume abbr or Roman Numeral) // we only care about this for numbers and identifiers/text, so we filter for only those - let node_name = name(node); let is_in_enclosed_list = node_name != "mo" && BrailleChars::is_in_enclosed_list(node); let is_mn_in_enclosed_list = is_in_enclosed_list && node_name == "mn"; let mut typeface = "R".to_string(); // assumption is "R" and if attr or letter is different, something happens @@ -2760,7 +2769,15 @@ impl BrailleChars { // debug!(" face: {:?}, lang: {:?}, num {:?}, letter: {:?}, cap: {:?}, char: {:?}", // &caps["face"], &caps["lang"], &caps["num"], &caps["letter"], &caps["cap"], &caps["char"]); let mut nemeth_chars = "".to_string(); - let char_face = if caps["face"].is_empty() {attr_typeface} else {&caps["face"]}; + let face_stripped; + let char_face = if caps["face"].is_empty() { + attr_typeface + } else if italic_ok { + &caps["face"] + } else { + face_stripped = caps["face"].replace('I', ""); + &face_stripped + }; let typeface_changed = typeface != char_face; if typeface_changed { typeface = char_face.to_string(); // needs to outlast this instance of the loop @@ -2813,11 +2830,16 @@ impl BrailleChars { return Ok(braille_chars); } // mathvariant could be "sans-serif-bold-italic" -- get the parts + // Italic typeform for digits (GTM 2.7); math letters do not get italic (GTM 1.5). let math_variant = math_variant.unwrap(); - let italic = math_variant.contains("italic"); - if italic & !braille_chars.contains('I') { + let italic_ok = BrailleChars::uses_italic_typeform(name(node), &text); + let italic = italic_ok && math_variant.contains("italic"); + if italic && !braille_chars.contains('I') { braille_chars = "I".to_string() + &braille_chars; } + if !italic_ok { + braille_chars = braille_chars.replace('I', ""); + } let bold = math_variant.contains("bold"); if bold & !braille_chars.contains('B') { braille_chars = "B".to_string() + &braille_chars; @@ -2850,11 +2872,15 @@ impl BrailleChars { fn get_braille_russian_chars(node: Element, text_range: Option>) -> Result { let text = BrailleChars::substring(as_str!(as_text(node)), &text_range); - let braille_chars = braille_replace_chars(&text, node)?; + let mut braille_chars = braille_replace_chars(&text, node)?; let Some(raw_math_variant) = node.attribute_value("mathvariant") else { - return Ok(braille_chars); + return Ok(braille_chars.replace('I', "")); }; let math_variant = as_str!(raw_math_variant); + let italic_ok = BrailleChars::uses_italic_typeform(name(node), &text); + if !italic_ok { + braille_chars = braille_chars.replace('I', ""); + } let mut prefix = String::new(); let mut suffix = String::new(); @@ -2862,7 +2888,8 @@ impl BrailleChars { prefix.push('⠻'); suffix.insert(0, '⠻'); } - if math_variant.contains("italic") && !braille_chars.contains('I') { + // Italic typeform only for digits (not math letters) + if math_variant.contains("italic") && italic_ok && !braille_chars.contains('I') { prefix.push('⠸'); suffix.insert(0, '⠸'); } @@ -2898,16 +2925,27 @@ impl BrailleChars { let text = BrailleChars::substring(as_str!(as_text(node)), &text_range); let text = add_separator(text); - let braille_chars = braille_replace_chars(&text, node)?; + let mut braille_chars = braille_replace_chars(&text, node)?; // debug!("get_braille_ueb_chars: before/after unicode.yaml: '{}'/'{}'", text, braille_chars); if math_variant.is_none() { // nothing we need to do return Ok(braille_chars); } // mathvariant could be "sans-serif-bold-italic" -- get the parts + // CMU: italic typeform for digits only (not math letters) let math_variant = math_variant.unwrap(); + let italic_ok = BrailleChars::uses_italic_typeform(name(node), &text); + let italic = italic_ok && math_variant.contains("italic"); + if italic && !braille_chars.contains('I') { + braille_chars = "I".to_string() + &braille_chars; + } + if !italic_ok { + braille_chars = braille_chars.replace('I', ""); + } let bold = math_variant.contains("bold"); - let italic = math_variant.contains("italic"); + if bold && !braille_chars.contains('B') { + braille_chars = "B".to_string() + &braille_chars; + } let typeface = match HAS_TYPEFACE.find(math_variant) { None => "", Some(m) => match m.as_str() { diff --git a/tests/braille/UEB/other.rs b/tests/braille/UEB/other.rs index 35c7cfcf0..b31f9958f 100644 --- a/tests/braille/UEB/other.rs +++ b/tests/braille/UEB/other.rs @@ -227,3 +227,31 @@ fn unicode_subscript_chars_in_and_out_of_script_position() -> Result<()> { return Ok(()); } +#[test] +fn ueb_no_italic_typeform_for_math_letters() -> Result<()> { + // UEB omits italic typeform for math letters (GTM 1.5); Greek uses ⠨ via G only. + // Plain, mathvariant italic, and Mathematical Italic x should match (no italic ⠨). + let plain = "x"; + let attr_italic = "x"; + let plane1_italic = "𝑥"; + test_braille("UEB", plain, "⠰⠭")?; + test_braille("UEB", attr_italic, "⠰⠭")?; + test_braille("UEB", plane1_italic, "⠰⠭")?; + + // Greek letter indicator (same cell as italic typeform) still used for α + test_braille("UEB", "α", "⠨⠁")?; + + // Bold-italic keeps bold ⠘, not italic typeform + test_braille("UEB", "x", "⠘⠆⠰⠭")?; + test_braille("UEB", "𝒙", "⠘⠆⠰⠭")?; + return Ok(()); +} + +#[test] +fn ueb_italic_typeform_for_digits() -> Result<()> { + // UEB uses italic typeform for digits (GTM 2.7 emphasis of numeric material). + test_braille("UEB", "3", "⠨⠆⠼⠉")?; + test_braille("UEB", ".3", "⠨⠂⠼⠲⠉")?; + return Ok(()); +} + From fbc49bb73cc1b6915f621fa8a63c39c306bdd852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Gro=C3=9F?= Date: Sun, 16 Aug 2026 09:52:39 +0200 Subject: [PATCH 033/120] Announce column separator (#679) * Add `HasVisibleColumnLine` XPath function to handle table separators in speech output - Replace `count_table_dims` return type with `usize` values for clarity. - Update function registration to include `HasVisibleColumnLine`. - Modify speech tests to reflect separator usage in matrix descriptions. * Refactor `count_table_dims` to return `Result` type and update usage sites * undo whitespace change * . * . * explain tests better in comments * explain tests better in comments * Add support for row separators and unify boundary line logic * fix check_table_line helper function * added a pause before separator announcements * Change pause duration from medium to short * Update test cases for matrix representations to add pause before separator --------- Co-authored-by: NSoiffer --- Rules/Languages/en/SharedRules/default.yaml | 11 ++ src/speech.rs | 2 +- src/xpath_functions.rs | 190 +++++++++++++++++++- tests/Languages/en/mtable.rs | 77 +++++++- 4 files changed, 269 insertions(+), 11 deletions(-) diff --git a/Rules/Languages/en/SharedRules/default.yaml b/Rules/Languages/en/SharedRules/default.yaml index 255c3bbf4..264c14540 100644 --- a/Rules/Languages/en/SharedRules/default.yaml +++ b/Rules/Languages/en/SharedRules/default.yaml @@ -478,6 +478,17 @@ - x: "count(preceding-sibling::*)+IfThenElse(parent::m:mlabeledtr, 0, 1)" - pause: medium - x: "*" + - test: + if: "HasVisibleColumnLine(../.., count(preceding-sibling::*) + 1)" + then: + - pause: short + - t: "separator" + - 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: "row separator" - 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 diff --git a/src/speech.rs b/src/speech.rs index 0aaf71405..eaaeff282 100644 --- a/src/speech.rs +++ b/src/speech.rs @@ -1761,7 +1761,7 @@ impl<'c, 'r> ContextStack<'c> { fn base_context(var_defs: PreferenceHashMap) -> sxd_xpath_no_unsafe::Context<'c> { let mut context = sxd_xpath_no_unsafe::Context::new(); context.set_namespace("m", "http://www.w3.org/1998/Math/MathML"); - crate::xpath_functions::add_builtin_functions(&mut context); + crate::xpath_functions::register_mathcat_xpath_functions(&mut context); for (key, value) in var_defs { context.set_variable(key.as_str(), yaml_to_value(&value)); // if let Some(str_value) = value.as_str() { diff --git a/src/xpath_functions.rs b/src/xpath_functions.rs index 6dc58aeac..587206f44 100644 --- a/src/xpath_functions.rs +++ b/src/xpath_functions.rs @@ -1646,9 +1646,86 @@ impl Function for CountTableColumns { } } +#[derive(Clone, Copy)] +enum TableLineAxis { + Row, + Column, +} + +/// Return whether a one-based mtable boundary has a visible line on the given axis. +/// +/// MathML repeats the final line style for remaining boundaries. Boundaries after +/// the final row or column, and values other than `solid` and `dashed`, do not +/// describe a visible separator. +fn has_visible_table_line(table: Element, boundary: usize, axis: TableLineAxis) -> bool { + if boundary == 0 || !is_tag(table, "mtable") { + return false; + } + + let Ok((Value::Number(row_count), Value::Number(column_count))) = CountTableDims::new().count_table_dims(table) else { + return false; + }; + let (line_count, attribute_name) = match axis { + TableLineAxis::Row => (row_count, "rowlines"), + TableLineAxis::Column => (column_count, "columnlines"), + }; + if boundary as f64 >= line_count { + return false; + } + + return table + .attribute_value(attribute_name) + .map(|values| { + matches!( + values.split_whitespace().take(boundary).last(), + Some("solid" | "dashed") + ) + }) + .unwrap_or(false); +} + +/// Validate and convert XPath arguments before delegating to the typed table-line helper. +fn evaluate_has_visible_table_line<'d>( + args: Vec>, + function_name: &str, + axis: TableLineAxis, +) -> Result, Error> { + let mut args = Args(args); + args.exactly(2)?; + let boundary = args.pop_number()?; + let table = validate_one_node(args.pop_nodeset()?, function_name)?; + let Node::Element(table) = table else { + return Err(Error::Other { what: format!("{function_name} requires an mtable element") }); + }; + if !boundary.is_finite() || boundary < 1.0 || boundary.fract() != 0.0 { + return Ok(Value::Boolean(false)); + } + return Ok(Value::Boolean(has_visible_table_line(table, boundary as usize, axis))); +} + +/// XPath function reporting whether an mtable column boundary has a visible line. +struct HasVisibleColumnLine; +impl Function for HasVisibleColumnLine { + fn evaluate<'c, 'd>(&self, + _context: &context::Evaluation<'c, 'd>, + args: Vec>) -> Result, Error> { + evaluate_has_visible_table_line(args, "HasVisibleColumnLine", TableLineAxis::Column) + } +} + +/// XPath function reporting whether an mtable row boundary has a visible line. +struct HasVisibleRowLine; +impl Function for HasVisibleRowLine { + fn evaluate<'c, 'd>(&self, + _context: &context::Evaluation<'c, 'd>, + args: Vec>) -> Result, Error> { + evaluate_has_visible_table_line(args, "HasVisibleRowLine", TableLineAxis::Row) + } +} + /// Add all the functions defined in this module to `context`. -pub fn add_builtin_functions(context: &mut Context) { +pub fn register_mathcat_xpath_functions(context: &mut Context) { context.set_function("NestingChars", crate::braille::NemethNestingChars); context.set_function("BrailleChars", crate::braille::BrailleChars); context.set_function("NeedsToBeGrouped", crate::braille::NeedsToBeGrouped); @@ -1671,6 +1748,8 @@ pub fn add_builtin_functions(context: &mut Context) { context.set_function("GetNavigationPartName", GetNavigationPartName); context.set_function("CountTableRows", CountTableRows); context.set_function("CountTableColumns", CountTableColumns); + context.set_function("HasVisibleColumnLine", HasVisibleColumnLine); + context.set_function("HasVisibleRowLine", HasVisibleRowLine); context.set_function("DEBUG", Debug); // Not used: remove?? @@ -1876,7 +1955,10 @@ mod tests { let package = parser::parse(mathml).map_err(|e| anyhow::anyhow!("failed to parse XML: {e}"))?; let math_elem = get_element(&package); let child = as_element(math_elem.children()[0]); - assert!(CountTableDims::new().count_table_dims(child) == Ok((Value::Number(dims.0 as f64), Value::Number(dims.1 as f64)))); + assert_eq!( + CountTableDims::new().count_table_dims(child), + Ok((Value::Number(dims.0 as f64), Value::Number(dims.1 as f64))) + ); return Ok( () ); } @@ -1896,6 +1978,110 @@ mod tests { }); } + fn check_table_line(mathml: &str, boundary: usize, axis: TableLineAxis, expected: bool) -> Result<()> { + let package = parser::parse(mathml).map_err(|e| anyhow::anyhow!("failed to parse XML: {e}"))?; + let math = get_element(&package); + let table = math + .children() + .iter() + .find_map(|child| match child { + ChildOfElement::Element(table) => Some(*table), + _ => None, + }) + .expect("test MathML should contain an mtable element"); + assert_eq!(has_visible_table_line(table, boundary, axis), expected); + return Ok(()); + } + + /// Verifies visible table-line styles, repeated styles, and boundaries outside the table. + #[test] + fn visible_table_lines() -> Result<()> { + return xpath_test(|| { + // The three values map in order to the three boundaries between four columns. + let mixed_column_lines: &str = " + + + column 1 + column 2 + column 3 + column 4 + + "; + check_table_line(mixed_column_lines, 1, TableLineAxis::Column, false)?; + check_table_line(mixed_column_lines, 2, TableLineAxis::Column, true)?; + check_table_line(mixed_column_lines, 3, TableLineAxis::Column, true)?; + + // The final `dashed` value repeats for the third column boundary. + let repeated_column_line: &str = " + + + column 1 + column 2 + column 3 + column 4 + + "; + check_table_line(repeated_column_line, 3, TableLineAxis::Column, true)?; + + // A table without `columnlines` has no visible column boundary. + let no_column_lines: &str = " + + + column 1 + column 2 + + "; + check_table_line(no_column_lines, 1, TableLineAxis::Column, false)?; + + // `none` explicitly makes the column boundary invisible. + let invisible_column_line: &str = " + + + column 1 + column 2 + + "; + check_table_line(invisible_column_line, 1, TableLineAxis::Column, false)?; + + // Only `solid` and `dashed` describe visible table lines. + let unsupported_column_line: &str = " + + + column 1 + column 2 + + "; + check_table_line(unsupported_column_line, 1, TableLineAxis::Column, false)?; + + // Boundary 2 is after the final column, not between two columns. + let two_column_table: &str = " + + column 1 + column 2 + + "; + check_table_line(two_column_table, 2, TableLineAxis::Column, false)?; + + // Four rows have three interior boundaries. `none` applies after row 1, + // `dashed` applies after row 2, and the final `dashed` repeats after row 3. + let mixed_row_lines: &str = " + + row 1 + row 2 + row 3 + row 4 + "; + check_table_line(mixed_row_lines, 1, TableLineAxis::Row, false)?; + check_table_line(mixed_row_lines, 2, TableLineAxis::Row, true)?; + check_table_line(mixed_row_lines, 3, TableLineAxis::Row, true)?; + // Boundary 4 is after the final row, not between two rows. + check_table_line(mixed_row_lines, 4, TableLineAxis::Row, false)?; + // Boundary zero is invalid for both axes. + check_table_line(mixed_row_lines, 0, TableLineAxis::Row, false)?; + return Ok(()); + }); + } + #[test] fn at_left_edge() -> Result<()> { return xpath_test(|| { diff --git a/tests/Languages/en/mtable.rs b/tests/Languages/en/mtable.rs index e6952cf51..8a670988e 100644 --- a/tests/Languages/en/mtable.rs +++ b/tests/Languages/en/mtable.rs @@ -271,8 +271,69 @@ fn augmented_matrix_2x3() -> Result<()> { ] "; - test("en", "ClearSpeak", expr, "the 2 by 3 augmented matrix; row 1; 3, 1, 4; row 2; 0, 2, 6")?; - test("en", "SimpleSpeak", expr, "the 2 by 3 augmented matrix; row 1; 3, 1, 4; row 2; 0, 2, 6")?; + test("en", "ClearSpeak", expr, "the 2 by 3 augmented matrix; row 1; 3, 1, separator, 4; row 2; 0, 2, separator, 6")?; + test("en", "SimpleSpeak", expr, "the 2 by 3 augmented matrix; row 1; 3, 1, separator, 4; row 2; 0, 2, separator, 6")?; + Ok(()) +} + +#[test] +fn dashed_augmented_matrix_separator() -> Result<()> { + let expr = " + + [ + + 123 + + ] + "; + test("en", "ClearSpeak", expr, "the 1 by 3 row matrix; 1, separator, 2, separator, 3")?; + test("en", "SimpleSpeak", expr, "the 1 by 3 row matrix; 1, separator, 2, separator, 3")?; + Ok(()) +} + +/// A horizontal line is announced once, after the row it separates from the next row. +#[test] +fn matrix_row_separator() -> Result<()> { + let expr = " + + [ + + + 1 + 2 + + + 3 + 4 + + + ] + "; + test("en", "ClearSpeak", expr, "the 2 by 2 matrix; row 1; 1, 2, row separator; row 2; 3, 4")?; + test("en", "SimpleSpeak", expr, "the 2 by 2 matrix; row 1; 1, 2, row separator; row 2; 3, 4")?; + Ok(()) +} + +/// Horizontal and vertical lines use distinct announcements at their respective boundaries. +#[test] +fn matrix_row_and_column_separators() -> Result<()> { + let expr = " + + [ + + + 1 + 2 + + + 3 + 4 + + + ] + "; + test("en", "ClearSpeak", expr, "the 2 by 2 augmented matrix; row 1; 1, separator, 2, row separator; row 2; 3, separator, 4")?; + test("en", "SimpleSpeak", expr, "the 2 by 2 augmented matrix; row 1; 1, separator, 2, row separator; row 2; 3, separator, 4")?; Ok(()) } @@ -926,13 +987,13 @@ let expr = " "; test_ClearSpeak("en", "ClearSpeak_Matrix", "EndMatrix", - expr, "the 3 by 4 augmented matrix; row 1; column 1; 1, column 2; 2, column 3; negative 1, column 4; 3; \ - row 2; column 1; negative 3, column 2; 3, column 3; negative 1, column 4; 2; \ - row 3; column 1; 2, column 2; 3, column 3; 2, column 4; negative 1; end matrix")?; + expr, "the 3 by 4 augmented matrix; row 1; column 1; 1, column 2; 2, column 3; negative 1, separator, column 4; 3; \ + row 2; column 1; negative 3, column 2; 3, column 3; negative 1, separator, column 4; 2; \ + row 3; column 1; 2, column 2; 3, column 3; 2, separator, column 4; negative 1; end matrix")?; test("en", "SimpleSpeak", - expr, "the 3 by 4 augmented matrix; row 1; column 1; 1, column 2; 2, column 3; negative 1, column 4; 3; \ - row 2; column 1; negative 3, column 2; 3, column 3; negative 1, column 4; 2; \ - row 3; column 1; 2, column 2; 3, column 3; 2, column 4; negative 1; end matrix")?; + expr, "the 3 by 4 augmented matrix; row 1; column 1; 1, column 2; 2, column 3; negative 1, separator, column 4; 3; \ + row 2; column 1; negative 3, column 2; 3, column 3; negative 1, separator, column 4; 2; \ + row 3; column 1; 2, column 2; 3, column 3; 2, separator, column 4; negative 1; end matrix")?; Ok(()) } From d5c63d879c29e03f8a16e6432ca37c62fc587cd7 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Sun, 16 Aug 2026 00:57:10 -0700 Subject: [PATCH 034/120] fix clippy warnings in the no-unsafe version --- src/braille.rs | 8 ++++---- src/interface.rs | 6 +++--- src/navigate.rs | 4 ++-- tests/Languages/en/definitions.rs | 12 ++++-------- tests/Languages/hu/definitions.rs | 6 ++---- 5 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/braille.rs b/src/braille.rs index 929bcf791..9453f5ddf 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -2737,7 +2737,7 @@ impl BrailleChars { let raw_math_variant = node.attribute_value("mathvariant"); let math_variant = raw_math_variant.as_deref(); let text = BrailleChars::substring(as_str!(as_text(node)), &text_range); - let node_name = name(node); + let node_name = as_str!(name(node)); let italic_ok = BrailleChars::uses_italic_typeform(node_name, &text); let attr_typeface = match math_variant { None => "R", @@ -2832,7 +2832,7 @@ impl BrailleChars { // mathvariant could be "sans-serif-bold-italic" -- get the parts // Italic typeform for digits (GTM 2.7); math letters do not get italic (GTM 1.5). let math_variant = math_variant.unwrap(); - let italic_ok = BrailleChars::uses_italic_typeform(name(node), &text); + let italic_ok = BrailleChars::uses_italic_typeform(as_str!(name(node)), &text); let italic = italic_ok && math_variant.contains("italic"); if italic && !braille_chars.contains('I') { braille_chars = "I".to_string() + &braille_chars; @@ -2877,7 +2877,7 @@ impl BrailleChars { return Ok(braille_chars.replace('I', "")); }; let math_variant = as_str!(raw_math_variant); - let italic_ok = BrailleChars::uses_italic_typeform(name(node), &text); + let italic_ok = BrailleChars::uses_italic_typeform(as_str!(name(node)), &text); if !italic_ok { braille_chars = braille_chars.replace('I', ""); } @@ -2934,7 +2934,7 @@ impl BrailleChars { // mathvariant could be "sans-serif-bold-italic" -- get the parts // CMU: italic typeform for digits only (not math letters) let math_variant = math_variant.unwrap(); - let italic_ok = BrailleChars::uses_italic_typeform(name(node), &text); + let italic_ok = BrailleChars::uses_italic_typeform(as_str!(name(node)), &text); let italic = italic_ok && math_variant.contains("italic"); if italic && !braille_chars.contains('I') { braille_chars = "I".to_string() + &braille_chars; diff --git a/src/interface.rs b/src/interface.rs index 6486aa762..012345056 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -1373,11 +1373,11 @@ mod tests { set_mathml(good_mathml)?; let bad_mathml = "&xabc;"; assert!(set_mathml(bad_mathml).is_err()); - assert!(get_spoken_text()? == ""); + assert!(get_spoken_text()?.is_empty()); set_mathml(good_mathml)?; let bad_mathml = "garbage"; assert!(set_mathml(bad_mathml).is_err()); - assert!(get_spoken_text()? == ""); + assert!(get_spoken_text()?.is_empty()); return Ok( () ); }); } @@ -1404,7 +1404,7 @@ mod tests { x-y "; - set_mathml(&expr)?; + set_mathml(expr)?; let speech = get_spoken_text()?; // Rule-generated SSML must pass through verbatim (not XML-entity-encoded). assert!(!speech.contains("<")); diff --git a/src/navigate.rs b/src/navigate.rs index 0ef0f62bc..f893b3838 100644 --- a/src/navigate.rs +++ b/src/navigate.rs @@ -1004,10 +1004,10 @@ mod tests { match do_navigate_command_string(mathml, command) { Err(e) => { panic!("\nStarting at '{}', '{} failed.\n{}", - start_id, command, &crate::interface::errors_to_string(&e)) + start_id, command, crate::interface::errors_to_string(&e)) }, Ok(nav_speech) => { - let nav_speech = nav_speech.trim_end_matches(&[' ', ',', ';']); + let nav_speech = nav_speech.trim_end_matches([' ', ',', ';']); // debug!("Full speech: {}", nav_speech); if !result_id.is_empty() { let (id, _) = nav_stack.borrow().get_navigation_mathml_id(mathml); diff --git a/tests/Languages/en/definitions.rs b/tests/Languages/en/definitions.rs index 80197c8c3..adbae499c 100644 --- a/tests/Languages/en/definitions.rs +++ b/tests/Languages/en/definitions.rs @@ -597,13 +597,11 @@ fn linear_algebra_tests() -> Result<()> { " .to_string() } - "transpose" => format!( - " + "transpose" => " x - " - ), + ".to_string(), _ => format!( " @@ -713,14 +711,12 @@ fn geometry_prefix_tests() -> Result<()> { #[test] fn separator_tests() -> Result<()> { - let expr = format!( - " + let expr = " x y - " - ); + ".to_string(); test("en", "ClearSpeak", &expr, "x y")?; Ok(()) diff --git a/tests/Languages/hu/definitions.rs b/tests/Languages/hu/definitions.rs index bd84fb155..542f41885 100644 --- a/tests/Languages/hu/definitions.rs +++ b/tests/Languages/hu/definitions.rs @@ -705,14 +705,12 @@ fn geometry_prefix_tests() -> Result<()> { #[test] fn separator_tests() -> Result<()> { - let expr = format!( - " + let expr = " x y - " - ); + ".to_string(); test("hu", "ClearSpeak", &expr, "x y")?; Ok(()) From 78744ec32f52433ffd938b70d04e24705898fd06 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Sun, 16 Aug 2026 01:05:32 -0700 Subject: [PATCH 035/120] Revert changes to file from 6ecc412 --- Rules/Braille/Russian/unicode-full.yaml | 58 ++++++++++++------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/Rules/Braille/Russian/unicode-full.yaml b/Rules/Braille/Russian/unicode-full.yaml index b562dc302..813669ecc 100644 --- a/Rules/Braille/Russian/unicode-full.yaml +++ b/Rules/Braille/Russian/unicode-full.yaml @@ -57,7 +57,7 @@ - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 - - tc: "BC" + - tc: "B" - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒𝛓𝛔𝛕𝛖𝛗𝛘𝛙𝛚', 'αβγδεζηθικλμνξοπρςστυφχψω')" @@ -123,23 +123,23 @@ - spell: "translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz')" - "𝐴-𝑍": # 0x1d434 - 0x1d44d - - tc: "C" + - tc: "IC" - spell: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'abcdefghijklmnopqrstuvwxyz')" - "𝑎-𝑧": # 0x1d44e - 0x1d467 - - tc: "" + - tc: "I" - spell: "translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔𝑕𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz')" - "𝑨-𝒁": # 0x1d468 - 0x1d481 - - tc: "BC" + - tc: "BIC" - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'abcdefghijklmnopqrstuvwxyz')" - "𝒂-𝒛": # 0x1d482 - 0x1d49b - - tc: "B" + - tc: "BI" - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 - - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'abcdefghijklmnopqrstuvwxyz')" + - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "𝖺-𝗓": # 0x1d5ba - 0x1d5d3 - spell: "translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz')" @@ -148,7 +148,7 @@ - test: if: "not($IgnoreBold)" then: [tc: "B"] - - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'abcdefghijklmnopqrstuvwxyz')" + - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "𝗮-𝘇": # 0x1d5ee - 0x1d607 - test: @@ -157,27 +157,27 @@ - spell: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" - "𝘈-𝘡": # 0x1d608 - 0x1d621 - - tc: "C" - - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'abcdefghijklmnopqrstuvwxyz')" + - tc: "IC" + - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "𝘢-𝘻": # 0x1d622 - 0x1d63b - - tc: "" + - tc: "I" - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" - "𝘼-𝙕": # 0x1d63c - 0x1d655 - # - tc: "bold italicC" + # - tc: "bold italic" - test: if: "$IgnoreBold" - then: [tc: "C"] - else: [tc: "B"] - - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'abcdefghijklmnopqrstuvwxyz')" + then: [tc: I] + else: [tc: "BI"] + - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "𝙖-𝙯": # 0x1d656 - 0x1d66f # - tc: "bold italic" - test: if: "$IgnoreBold" - then: [tc: ""] - else: [tc: "B"] + then: [tc: I] + else: [tc: "BI"] - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" - "𝙰-𝚉": # 0x1d670 - 0x1d689 @@ -194,7 +194,7 @@ - test: if: "not($IgnoreBold)" then: [tc: "B"] - - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'αβγδεζηθικλμνξοπρςστυφχψω')" + - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" - "𝛂-𝛚": # 0x1d6c2 - 0x1d6da - test: @@ -215,7 +215,7 @@ - spell: "translate('.', '𝛛𝛜𝛝𝛞𝛟𝛠𝛡', '∂εθκφρπ')" - "𝛢-𝛺": # 0x1d6e2 - 0x1d6fa - - tc: "C" + - tc: "IC" - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹𝛺', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝛼-𝜔": # 0x1d6fc - 0x1d714 @@ -230,20 +230,20 @@ - spell: "translate('.', '𝜕𝜖𝜗𝜘𝜙𝜚𝜛', '∂εθκφρπ')" - "𝜜-𝜴": # 0x1d71c - 0x1d734 - # - tc: "bold italicC" - - tc: 'BC⠁' + # - tc: "bold italic" + - tc: 'BIC⠁' - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳𝜴', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝜶-𝝎": # 0x1d736 - 0x1d74e - - tc: "B" + - tc: "BI" - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆𝝇𝝈𝝉𝝊𝝋𝝌𝝍𝝎', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝝏𝝐𝝑𝝒𝝓𝝔𝝕": # 0x1d74f - 0x1d755 - - tc: "B" + - tc: "BI" - spell: "translate('.', '𝝏𝝐𝝑𝝒𝝓𝝔𝝕', '∂εκθφρπ')" - "𝜵": # 0x1d735 - - tc: "B" + - tc: "BI" - spell: "translate('.', '𝜵', '∇')" - "𝝖-𝝮": # 0x1d756 - 0x1d76e @@ -263,19 +263,19 @@ - spell: "translate('.', '𝜵', '∇')" - "𝞐-𝞨": # 0x1d790 - 0x1d7a8 - # - tc: "bold italicC" + # - tc: "bold italic" - test: if: "$IgnoreBold" - then: [tc: "C"] - else: [tc: "B"] - - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'αβγδεζηθικλμνξοπρςστυφχψω')" + then: [tc: I] + else: [tc: "BI"] + - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" - "𝞪-𝟂": # 0x1d7aa - 0x1d7c2 # - tc: "bold italic" - test: if: "$IgnoreBold" - then: [tc: ""] - else: [tc: "B"] + then: [tc: I] + else: [tc: "BI"] - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺𝞻𝞼𝞽𝞾𝞿𝟀𝟁𝟂', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝟃𝟄𝟅𝟆𝟇𝟈𝟉": # 0x1d7c3 - 0x1d7c9 From a1fc8e0750e32aeae96e00eb463748c97389613d Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Sun, 16 Aug 2026 01:33:24 -0700 Subject: [PATCH 036/120] missed this change that needed to be reverted for Russian braille --- src/braille.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/braille.rs b/src/braille.rs index 9453f5ddf..129fb7290 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -2871,16 +2871,13 @@ impl BrailleChars { } fn get_braille_russian_chars(node: Element, text_range: Option>) -> Result { + // Russian italic typeform for letters is left unchanged pending external advice. let text = BrailleChars::substring(as_str!(as_text(node)), &text_range); - let mut braille_chars = braille_replace_chars(&text, node)?; + let braille_chars = braille_replace_chars(&text, node)?; let Some(raw_math_variant) = node.attribute_value("mathvariant") else { - return Ok(braille_chars.replace('I', "")); + return Ok(braille_chars); }; let math_variant = as_str!(raw_math_variant); - let italic_ok = BrailleChars::uses_italic_typeform(as_str!(name(node)), &text); - if !italic_ok { - braille_chars = braille_chars.replace('I', ""); - } let mut prefix = String::new(); let mut suffix = String::new(); @@ -2888,8 +2885,7 @@ impl BrailleChars { prefix.push('⠻'); suffix.insert(0, '⠻'); } - // Italic typeform only for digits (not math letters) - if math_variant.contains("italic") && italic_ok && !braille_chars.contains('I') { + if math_variant.contains("italic") && !braille_chars.contains('I') { prefix.push('⠸'); suffix.insert(0, '⠸'); } From bc712eb8bed3442d2cfa90bfd8268cbf2d7537b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Gro=C3=9F?= Date: Sun, 16 Aug 2026 15:52:22 +0200 Subject: [PATCH 037/120] fix AGENTS.md instruction on `uv run pytest` --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 0b10d8a5f..0696e6979 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,7 +26,7 @@ but add common mistakes of AI agents here instead. - In sandboxed runs, if needed: - set `UV_CACHE_DIR=/tmp/uv-cache` - rerun with escalated permissions if macOS `system-configuration` panics occur. -- *always* self-validate: `ùv run pytest` +- self-validate appropriately with: `uv run pytest` ## Agent Instructions - Do not mirror README content here; keep guidance agent-specific. From 6937925c0ec6aa4fbc001555219c7c3f8fc2efcd Mon Sep 17 00:00:00 2001 From: NSoiffer Date: Sun, 16 Aug 2026 22:48:04 -0700 Subject: [PATCH 038/120] Merge latest hu changes (#696) * Add few tweaks into the Rules/Languages/hu/definitions.yaml file, Added missing definitions.rs test file the test/Languages/hu directory, and added SimpleSpeak test directory the mroot and menclose tests (#646) * Do more localization in Rules/Languages/hu/definitions.yaml Signed-off-by: Attila Hammer * Add in Simplespeak tests the definition, mroot and menclose tests Signed-off-by: Attila Hammer * fix some werbs in intervals into rules/Languages/hu/ClearSpeak_rules.yaml and SimpleSpeak_rules.yaml Signed-off-by: Attila Hammer * Fix tests after interval Clearspeak and SimpleSpeak verb change Signed-off-by: Attila Hammer --------- Signed-off-by: Attila Hammer * Apply Hungarian unicode fixes from PR #673 * Update Hungarian language and test rules from PR #694 * Fix a mistake after the selective merge with definitions.yaml and definition.rs test file (#697) Signed-off-by: Attila Hammer --------- Signed-off-by: Attila Hammer Co-authored-by: hammera --- Rules/Languages/hu/ClearSpeak_Rules.yaml | 18 +- Rules/Languages/hu/SharedRules/default.yaml | 71 +++--- Rules/Languages/hu/SharedRules/general.yaml | 4 +- Rules/Languages/hu/SharedRules/geometry.yaml | 3 +- Rules/Languages/hu/definitions.yaml | 96 ++++++-- Rules/Languages/hu/navigate.yaml | 57 +++-- Rules/Languages/hu/unicode-full.yaml | 207 +++++++++++++++++- tests/Languages/hu/ClearSpeak/sets.rs | 18 +- .../hu/ClearSpeak/symbols_and_adornments.rs | 8 +- tests/Languages/hu/definitions.rs | 24 +- tests/Languages/hu/shared.rs | 6 +- 11 files changed, 398 insertions(+), 114 deletions(-) diff --git a/Rules/Languages/hu/ClearSpeak_Rules.yaml b/Rules/Languages/hu/ClearSpeak_Rules.yaml index 73d8eaf5e..434034698 100644 --- a/Rules/Languages/hu/ClearSpeak_Rules.yaml +++ b/Rules/Languages/hu/ClearSpeak_Rules.yaml @@ -568,27 +568,25 @@ - name: set tag: set - match: "." + # 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 + # ("the set of …"). + match: "count(*)=0 or (count(*)=1 and not(*[1][@arg]))" replace: - test: - if: "count(*)=0" then: [T: "az üres halmaz"] # phrase('the empty set') - - else_if: "count(*)=2" - then: - - test: - if: "$Verbosity!='Terse'" - then: [T: "az"] # phrase('the' empty set) - - T: "üres halmaz" # phrase(the 'empty set') - else_if: "count(*[1]/*)=3 and *[1]/*[2][self::m:mo][.=':' or .='|' or .='∣']" then: - test: if: "$Verbosity!='Terse'" - then: [T: ""] # phrase('the' set of all integers) - - x: "*[1]/*[1]" - - T: "melyekre teljesül, hogy" # phrase(this is a 'set of' numbers) + then: [T: ""] # phrase('the' empty set) + - T: "" # phrase(the 'empty set') - test: if: "$ClearSpeak_Sets != 'woAll'" then: [T: "minden"] # phrase(the set of 'all' integers) + - x: "*[1]/*[1]" + - T: "melyekre teljesül, hogy" # phrase(the set S 'such that' x is less than y) - x: "*[1]/*[3]" else: - test: diff --git a/Rules/Languages/hu/SharedRules/default.yaml b/Rules/Languages/hu/SharedRules/default.yaml index b218667df..54bbffc64 100644 --- a/Rules/Languages/hu/SharedRules/default.yaml +++ b/Rules/Languages/hu/SharedRules/default.yaml @@ -62,7 +62,7 @@ - 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 > 0 or @data-roman-numeral" # NavMode=Character needs this + - 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) @@ -237,26 +237,12 @@ - x: "*[2]" - T: "alatt" # phrase(x with z 'below' it) -# FIX: this only works for up to four dots (which is realistically all we need) -- name: dot-over - tag: modified-variable - match: "*[1][self::m:mi] and (*[2][translate(., '.', '')=''])" - replace: - - x: "*[1]" - - x: "IfThenElse(string-length(*[2])=1, 'pont', - IfThenElse(string-length(*[2])=2, 'pont pont', - IfThenElse(string-length(*[2])=3, 'pont pont pont', 'pont pont pont pont') - ))" - - pause: short - -# FIX: the 'translate' list is a little different from 'modified-variable', so we also include 'mover' (unify?) - name: diacriticals - tag: [mover, modified-variable] + tag: mover match: "*[1][self::m:mi] and *[2][translate(., '\u0306\u030c.\u00A8\u02D9\u20DB\u20DC`^ˇ~→¯_', '')='']" replace: - x: "*[1]" - x: "*[2]" - - pause: short - name: default tag: mover @@ -434,23 +420,22 @@ - T: "szkriptek vége" # phrase(At this point 'end scripts' occurs) - name: default - tag: [mtable, array] + tag: mtable variables: - IsColumnSilent: "false()" - - NumColumns: "CountTableColumns(.)" - - NumRows: "CountTableRows(.)" + - NumColumns: "count(*[1]/*) - IfThenElse(*/self::m:mlabeledtr, 1, 0)" match: "." replace: - T: "táblázat" # phrase(the 'table with' 3 rows) - - x: "$NumRows" + - x: count(*) - test: - if: "$NumRows=1" + if: count(*)=1 then: [T: "sorral"] # phrase(the table with 1 'row') else: [T: "sorral"] # phrase(the table with 3 'rows') - T: "és" # phrase(the table with 3 rows 'and' 4 columns) - x: "$NumColumns" - test: - if: "$NumColumns=1" + if: "NumColumns=1" then: [T: "oszloppal"] # phrase(the table with 3 rows and 1 'column') else: [T: "oszloppal"] # phrase(the table with 3 rows and 4 'columns') - pause: long @@ -491,6 +476,18 @@ - x: "count(preceding-sibling::*)+IfThenElse(parent::m:mlabeledtr, 0, 1)" - pause: medium - x: "*" + - test: + # short pause after each element; medium pause if last element in a row; long pause for last element in matrix + if: "HasVisibleColumnLine(../.., count(preceding-sibling::*) + 1)" + then: + - pause: short + - T: "elválasztó" + - 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: "sorelválasztó" - 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 @@ -500,6 +497,7 @@ 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 @@ -720,16 +718,27 @@ replace: - x: "SpeakIntentName(name(.), $Verbosity, 'function')" - test: - if: "$Verbosity != 'Terse' and not(contains(@data-intent-property, ':literal:')) and - not(count(*)=2 and (IsInDefinition(*[1], 'TrigFunctionNames') or IsInDefinition(name(.), 'TerseFunctionNames')) and IsNode(*[2], 'simple'))" - then: [T: "", pause: auto] # phrase(sine 'of' 5) - - insert: - nodes: "*" - replace: - - test: - if: "not(contains(@data-intent-property, ':literal:'))" - then: [x: "','"] + - 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] # phrase(sine 'of' 5) + - 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')] )" diff --git a/Rules/Languages/hu/SharedRules/general.yaml b/Rules/Languages/hu/SharedRules/general.yaml index 0637bf6e2..9013205e2 100644 --- a/Rules/Languages/hu/SharedRules/general.yaml +++ b/Rules/Languages/hu/SharedRules/general.yaml @@ -70,14 +70,14 @@ - name: real-part tag: real-part - match: "." + match: "count(*)=0" replace: - bookmark: "@id" - T: "valós rész" # phrase('the real part' of a complex number does not include the imaginary part) - name: imaginary-part tag: imaginary-part - match: "." + match: "count(*)=0" replace: - bookmark: "@id" - T: "képzetes rész" # phrase('the imaginary part' is part of a complex number) diff --git a/Rules/Languages/hu/SharedRules/geometry.yaml b/Rules/Languages/hu/SharedRules/geometry.yaml index c23f22526..c5a0ac601 100644 --- a/Rules/Languages/hu/SharedRules/geometry.yaml +++ b/Rules/Languages/hu/SharedRules/geometry.yaml @@ -60,7 +60,8 @@ - name: coordinate tag: coordinate - match: "." +# Intent forms coordinate($x,...) use @arg and fall through to function-intent / IntentMappings + match: "not(*[@arg])" replace: - test: if: "$Verbosity='Verbose'" diff --git a/Rules/Languages/hu/definitions.yaml b/Rules/Languages/hu/definitions.yaml index bdc649424..d66738ec4 100644 --- a/Rules/Languages/hu/definitions.yaml +++ b/Rules/Languages/hu/definitions.yaml @@ -1,14 +1,71 @@ --- - include: "../../definitions.yaml" -# If an "intent" is used, the 'terse:medium:verbose' speech for the intent name is given here for a prefix||infix||postfix||function fixity -# If only one ":" is used, the first part is used for 'terse' and the second part is used for 'medium' and 'verbose' -# If no ":"s are used, the same speech is used for all forms -# If bracketing words make sense, they are separated with ";"s -# Intent mappings must specify whether they are "prefix", "infix", "postfix", or "function" with an "=" sign -# If there are multiple fixities (e.g., see transpose), they are separated with "||" -# for readability, spaces can be used around any of the delimiter characters -# Note: if there are multiple fixities, the first one is used if the fixity is not given in the intent +# 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: { "indexed-by": "infix= alsó index kezdete; alsó index; alsó index vége: alsó index vége: alsó index vége", "say-super": "infix=felső index: felső index: felső index", # used with 'mo' for superscripts (e.g, "<") @@ -30,14 +87,14 @@ #"fraction":"function=fraction; over; end fraction", # NOTE: Fails "mixed-fraction":"infix=és", # NOTE: in website says function, but follow infix speech pattern. "greatest-common-divisor": "function=a legnagyobb közös osztó", - "quotient":"function=egész rész osztva", # NOTE: Logic somewhere here failing, becomes "divided by of x comma, y" instead of "integer part of x divided by y" + "quotient":"function=egész rész | osztva", # NOTE: Logic somewhere here failing, becomes "divided by of x comma, y" instead of "integer part of x divided by y" "evaluated-at":"infix=értékelve || postfix=értékként", - "remainder":"function=a maradék, osztva", + "remainder":"function=a maradék | osztva", "max":"function=max", "min":"function=min", "power":"infix=a hatványra", "root":"function=root", - "imaginary-part": "a képzeletbeli rész", + "imaginary-part": "function=a képzeletbeli rész: a képzeletbeli rész: a képzeletbeli rész", "least-common-multiple": "function=lcm: a legkisebb közös többszörös: a legkisebb közös többszörös", "complex-conjugate":"function=Komplex konjugált", "complex-arg":"function=argumentum", @@ -74,15 +131,15 @@ "tends-to-from-below":"infix=alulról indul", ### Sets "set": "function= ; halmaz: a halmaz", - "set-difference":"function=különbséghalmaz || infix=mínusz || postfix=különbséghalmaza", # NOTE: not tested + "set-difference":"function=különbséghalmaz | és || infix=mínusz", "complement":"function=komplemens", - "empty-set":"function=üres halmaz", + "empty-set":"nofix=üres halmaz", "cardinality":"function=számosság", # NOTE: does not have a defined speech template in website "list":"function=lista", "tuple": "function= ; tuple: a tuple", ### Sequence and Series - "sum":"function= ; összegzés : összeg ; ", #: sum over : sum from; to", - "product":"function=szorzat || function=szorzat vége || function=szorzat innen ide", + "sum": "function=összeg", + "product":"function=szorzat", ### Elementary classical functions "sine":"function=szinusz: szinusz", @@ -118,7 +175,7 @@ "variance":"function=variencia", "median":"function=medián", "mode":"function=mód", - "conditional-probability":"function=valószínűség; adott", # NOTE: Check test + "conditional-probability":"function=valószínűség", ### Linear Algebra @@ -133,7 +190,7 @@ "unit-vector":"prefix=egységvektor", "identity-matrix":"nofix=identitásmátrix", # NOTE: no function specified - "transpose":"function=transzponált || postfix=transzponált", # postfix needs testing + "transpose":"postfix=transzponált || function=transzponált", "dimensional-product":"infix=a(z)", # INFIX @@ -164,10 +221,11 @@ ### General Concepts "fenced-group":"function=bekerített csoport", # 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= ; a pár; és", # Needs to be tested, test converts "and" to "comma," + "ordered-pair": "function=a pár | és", # Needs to be tested, test converts "and" to "comma," "highlight":"postfix=kiemelt", "least-common-denominator":"function=legkisebb közös nevező", "rate":"infix=per", + "translation":"function=fordítás", "constraint":"infix= ; feltétellel; ", "binomial-coefficient":"infix=választ", @@ -269,7 +327,7 @@ ## Default fixity nofix "diameter":"nofix=átmérő: átmérő", "distance":"nofix=távolság; távolság: távolság", - "probability":"nofix=P: valószínűség", + "probability":"nofix=P: valószínűség || function=valószínűség", "radius":"nofix=r: sugár", "volume":"nofix=hangerő: hangerő || function=hangerő", "exponential-e":"nofix=e", diff --git a/Rules/Languages/hu/navigate.yaml b/Rules/Languages/hu/navigate.yaml index ff4afb8bb..475e1f625 100644 --- a/Rules/Languages/hu/navigate.yaml +++ b/Rules/Languages/hu/navigate.yaml @@ -60,38 +60,40 @@ - 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: "'zoom'"]] # phrase('zoom' in to see more details) + then: [set_variables: [Prefix: "'zoom'", CommandOffset: "5"]] # phrase('zoom' in to see more details) - else_if: "starts-with($NavCommand, 'Move')" - then: [set_variables: [Prefix: "'move'"]] # phrase('move' to next entry in table) + then: [set_variables: [Prefix: "'move'", CommandOffset: "5"]] # phrase('move' to next entry in table) - else_if: "starts-with($NavCommand, 'Read')" - then: [set_variables: [Prefix: "'read'"]] # phrase('read' to next entry in table) + then: [set_variables: [Prefix: "'read'", CommandOffset: "5"]] # phrase('read' to next entry in table) - else_if: "starts-with($NavCommand, 'Describe')" - then: [set_variables: [Prefix: "'describe'"]] # phrase('describe' to next entry in table) + then: [set_variables: [Prefix: "'describe'", CommandOffset: "9"]] # phrase('describe' to next entry in table) - test: if: "$Prefix != ''" then: - x: "$Prefix" - test: - - if: "substring($NavCommand, string-length($Prefix)+1) = 'In'" + - if: "substring($NavCommand, $CommandOffset) = 'In'" then: [T: "ban"] # phrase(zoom 'in' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'InAll'" + - 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: "Teljesen betáplálva"] # phrase(zoom 'out all of the way' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Out'" + then: [T: "Teljesen kinagyítva"] # phrase(zoom 'out all of the way' to see more details) + - else_if: "substring($NavCommand, $CommandOffset) = 'Out'" then: [T: "ki"] # phrase(zoom 'out' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'OutAll'" + - 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: "teljes táblázat megjelenítése\uF8FEe ki egészen"] # phrase(zoom 'out all of the way' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Next'" + then: [T: "teljes táblázat kinagyítása"] # phrase(zoom 'out all of the way' to see more details) + - else_if: "substring($NavCommand, $CommandOffset) = 'Next'" then: [T: "jobbra"] # phrase(move to the 'right') - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Previous'" + - else_if: "substring($NavCommand, $CommandOffset) = 'Previous'" then: [T: "balra"] # phrase(move to the 'left') - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Current'" + - else_if: "substring($NavCommand, $CommandOffset) = 'Current'" then: [T: "jelenlegi"] # phrase(who is the 'current' president) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'LineStart'" + - else_if: "substring($NavCommand, $CommandOffset) = 'LineStart'" then: [T: "a sor elejére"] # phrase(move 'to start of line') - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'LineEnd'" + - else_if: "substring($NavCommand, $CommandOffset) = 'LineEnd'" then: [T: "a sor végére"] # phrase(move 'to end of line') - pause: "medium" - set_variables: [MatchCounter: "$MatchCounter + 1"] @@ -103,6 +105,15 @@ (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'" @@ -158,7 +169,21 @@ - else_if: "count(*) = 1" then_test: if: "$NavVerbosity = 'Verbose'" - then: [x: "translate(name(.), '-_', ' ')"] # e.g., "in absolute value" + 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: "része" # phrase(the 'part' of the expression) - x: "count($Child2D/preceding-sibling::*) + 1" diff --git a/Rules/Languages/hu/unicode-full.yaml b/Rules/Languages/hu/unicode-full.yaml index 4ab669c7a..23381591d 100644 --- a/Rules/Languages/hu/unicode-full.yaml +++ b/Rules/Languages/hu/unicode-full.yaml @@ -1279,17 +1279,10 @@ - T: "zárójeles" # (en: 'parenthesized', DeepL translation) - SPELL: "translate('.', '⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵', 'abcdefghijklmnopqrstuvwxyz')" - - "Ⓐ-Ⓩ": # 0x24b6 - 0x24cf - - T: "bekarikázott" - - SPELL: "translate('.', 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - "🅐-🅩": # 0x1f150 - 0x1f169 - T: "fekete körrel jelölt" # (en: 'black circled', DeepL translation) - SPELL: "translate('.', '🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - "ⓐ-ⓩ": # 0x24d0 - 0x24e9 - - T: "bekarikázott" # (en: 'circled', DeepL translation) - - SPELL: "translate('.', 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ', 'abcdefghijklmnopqrstuvwxyz')" - "⓪": [T: "bekarikázott nulla"] # 0x24ea (en: 'circled zero', DeepL translation) - "⓫": [T: "fekete kör alakú tizenegyes"] # 0x24eb (en: 'black circled eleven', DeepL translation) - "⓬": [T: "fekete bekarikázott tizenkettő"] # 0x24ec (en: 'black circled twelve', DeepL translation) @@ -3619,3 +3612,203 @@ - "\uE42C": [SPELL: "'←'"] # 0xe42c defer to def of arrow - "\uE42D": [SPELL: "'→'"] # 0xe42d defer to def of arrow - "\uE42E": [SPELL: "'⇄'"] # 0xe42e defer to def of arrow +# Following symbols hungarian translations are restored, because english unicode-full.yaml file again contains this symbols. +# Note: this restoration purpose only to audit translation tool doesn't show this difference with english unicode-full.yaml and hungarian unicode-full.yaml file. +# This symbols are defined too the hungarian unicode.yaml file. + - "¬": [T: "logikai nem"] # 0xac (en: 'not', DeepL translation) + - "´": [T: "éles ékezet"] # 0xb4 (en: 'acute', DeepL translation) + - "·": # 0xB7 + - test: + if: "$SpeechStyle != 'ClearSpeak' or $ClearSpeak_MultSymbolDot = 'Auto'" + then: [T: "szorozva"] # (en: 'dot', DeepL translation) + else: [T: "pont"] # (DeepL translation) + - "×": # 0xd7 + - test: + if: "$SpeechStyle != 'ClearSpeak' or $ClearSpeak_MultSymbolX = 'Auto'" + then: [T: "szorozva"] + else_test: + if: $ClearSpeak_MultSymbolX = 'By' + then: [T: "szorozva"] + else: [T: "kereszt"] # (en: 'cross', DeepL translation) + - "÷": [T: "osztva"] # 0xf7 (en: 'divided by', DeepL translation) + - "̀": [T: "súlyos ékezet díszítés"] # 0x300 (en: 'grave accent embellishment', DeepL translation) + - "́": [T: "akut ékezet díszítés"] # 0x301 (en: 'acute accent embellishment', DeepL translation) + - "̂": [T: "cirkumflex ékezet díszítés"] # 0x302 (en: 'circumflex accent embellishment', DeepL translation) + - "̃": [T: "tilde"] # 0x303 (en: 'tilde embellishment', DeepL translation) + - "̄": [T: "makron"] # 0x304 (en: 'macron embellishment', DeepL translation) + - "̅": [T: "túlszárnyaló díszítés"] # 0x305 (en: 'overbar embellishment', DeepL translation) + - "̆": [T: "rövid"] # 0x306 (DeepL translation) + - "̇": [T: "pont a díszítés felett"] # 0x307 (en: 'dot above embellishment', DeepL translation) + - "ϖ": [T: "pí"] # 0x3d6 (DeepL translation) + - "ϵ": [T: "holdkóros epszilon"] # 0x3f5 (DeepL translation) + - "϶": [T: "félhold alakú tükrözött görög epszilon"] # 0x3f6 (en: 'reversed epsilon', DeepL translation) + - "…": # 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: "pont pont pont"] # (en: 'dot dot dot', DeepL translation) + else_test: # must have $ClearSpeak_Ellipses = 'AndSoOn' + if: "count(following-sibling::*) = 0" + then: [T: "és így tovább"] # (en: 'and so on', DeepL translation) + else: [T: "és így tovább egészen az alábbi értékig:"] # (en: 'and so on up to', DeepL translation) + - "ℓ": [T: "alsó index l"] # 0x2113 + - "ϕ": [T: "phi"] # 0x3d5 (DeepL translation) + - "⇒": [T: "dupla jobbranyíl"] # 0x21d2 (en: 'right double arrow', DeepL translation) + - "∂": # 0x2202 + - test: + if: "$Verbosity='Terse'" + then: [T: "részleges"] # (en: 'partial', DeepL translation) + else: [T: "parciális derivált"] # (en: 'partial derivative', DeepL translation) + - "∆": # 0x2206 + - test: + if: "following-sibling::*" + then_test: + if: "$Verbosity!='Terse'" + then: [T: "egy laplaciánus"] # "LahPlahsian" sounds better than "laplacian" in speech engines tested + else: [T: "laplaciánus"] + else: [T: "laplaciánus"] + + + - "∉": # 0x2209 + # rule is identical to 0x2208 + - test: + if: "$SpeechStyle != 'ClearSpeak'" + then: [T: "nem eleme ennek:"] + # 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: "nincs benne:"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "nem tagja ennek:"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "nem eleme ennek:"] + - else: [T: "nem tartozik hozzá:"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + else_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "nem tagja az alábbi halmaznak:"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "nem eleme ennek:"] + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "nincs benne:"] + - else: [T: "nem tartozik hozzá:"] # $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: "van"] # (en: 'is', DeepL translation) + - T: "egy eleme" # (en: 'an element of', DeepL translation) + # 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: ""] # (DeepL translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "tagja ennek:"] # (en: 'member of', DeepL translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "eleme ennek:"] # (en: 'element of', DeepL translation) + - else: [T: "hozzátartozó eleme ennek:"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'belonging to', DeepL translation) + else_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "tagja az alábbi halmaznak:"] # (en: 'is a member of', DeepL translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "egy eleme ennek:"] # (en: 'is an element of', DeepL translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "benne van ebben:"] # (en: 'is in', DeepL translation) + - else: [T: "ebbe a halmazba tartozik:"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'belongs to', DeepL translation) + - "∐": [T: "diszjunkt unió"] # 0x2210 (en: 'co-product', DeepL translation) + - "√": # 0x221a + - test: + if: "$Verbosity!='Terse'" + then: [T: "a"] # (en: 'the', DeepL translation) + - T: "négyzetgyöke" # (en: 'square root of', DeepL translation) + - "∝": # 0x221d + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # (en: 'is', DeepL translation) + - T: "arányos" # (en: 'proportional to', DeepL translation) + - "∶": # 0x2236 + - test: + if: "$Verbosity!='Terse'" + then: [T: "van"] # (en: 'is', DeepL translation) + - T: "hogy" # (DeepL translation) + - "∾": # 0x223e + - test: + if: "$Verbosity!='Terse'" + then: [T: "van"] # (en: 'is', DeepL translation) + - T: "legpozitívabb" # (en: 'most positive', DeepL translation) + - "≠": # 0x2260 + - test: + if: "$Verbosity!='Terse'" + then: [T: "van"] # (en: 'is', DeepL translation) + - T: "nem egyenlő" # (en: 'not equal to', DeepL translation) + - "≡": # 0x2261 + - test: + if: "$Verbosity!='Terse'" + then: [T: "van"] # (en: 'is', DeepL translation) + - T: "azonos" # (en: 'identical to', DeepL translation) + - "⊂": # 0x2282 + - test: + if: "$Verbosity!='Terse'" + then: [T: "egy"] # (DeepL translation) + - T: "részhalmaza" # (en: 'subset of', DeepL translation) + - "⊃": # 0x2283 + - test: + if: "$Verbosity!='Terse'" + then: [T: "egy"] # (DeepL translation) + - T: "szuperhalmaza ennek:" # (en: 'superset of', DeepL translation) + - "⊄": # 0x2284 + - test: + if: "$Verbosity!='Terse'" + then: [T: "egy"] # (en: 'is', DeepL translation) + - T: "nem részhalmaza ennek:" # (en: 'not a subset of', DeepL translation) + - "⊅": # 0x2285 + - test: + if: "$Verbosity!='Terse'" + then: [T: "egy"] # (en: 'is', DeepL translation) + - T: "nem szuperhalmaza ennek:" # (DeepL translation) + - "⊆": # 0x2286 + - test: + if: "$Verbosity!='Terse'" + then: [T: "egy"] # (DeepL translation) + - T: "részhalmaza vagy egyenlő" # (en: 'subset of or equal to', DeepL translation) + - "⊇": # 0x2287 + - test: + if: "$Verbosity!='Terse'" + then: [T: "egy"] # (DeepL translation) + - T: "szuperhalmaza vagy egyenlő" # (en: 'superset of or equal to', DeepL translation) + - "ⅆ": [T: "d"] # 0x2146 + - "": [T: "e"] # 0xed11 + - "ⅈ": [T: "i"] # 0x2148 + - "-": # 0xf52e - 0xf537 (old MathType) + - test: + if: "not($IgnoreBold)" + then: [T: "félkövér"] + - SPELL: "translate('.', '', '0123456789')" + - "ⅇ": [T: "e"] # 0x2147 + - "Ⓐ-ⓩ": # 0x24b6 - 0x24cf + - T: "bekarikázott" + - SPELL: "translate('.', 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "ⓐ-ⓩ": # 0x24d0 - 0x24e9 + - T: "bekarikázott" + - SPELL: "translate('.', 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ', 'abcdefghijklmnopqrstuvwxyz')" + + + + + + + + + + + + diff --git a/tests/Languages/hu/ClearSpeak/sets.rs b/tests/Languages/hu/ClearSpeak/sets.rs index c2b9d589a..844e2a51c 100644 --- a/tests/Languages/hu/ClearSpeak/sets.rs +++ b/tests/Languages/hu/ClearSpeak/sets.rs @@ -223,7 +223,7 @@ fn set_with_colon() -> Result<()> { let expr = " { x:x>2 } "; - test("hu", "ClearSpeak", expr, "x melyekre teljesül, hogy minden x nagyobb, mint 2")?; + test("hu", "ClearSpeak", expr, "minden x melyekre teljesül, hogy x nagyobb, mint 2")?; return Ok(()); } @@ -234,7 +234,7 @@ fn set_with_bar() -> Result<()> { let expr = " { x|x>2 } "; - test("hu", "ClearSpeak", expr, "x melyekre teljesül, hogy minden x nagyobb, mint 2")?; + test("hu", "ClearSpeak", expr, "minden x melyekre teljesül, hogy x nagyobb, mint 2")?; return Ok(()); } @@ -285,7 +285,7 @@ fn complicated_set_with_colon() -> Result<()> { 7 } "; - test("hu", "ClearSpeak", expr, "x az egész számok halmaza melyekre teljesül, hogy minden 2 kisebb, mint x kisebb, mint 7")?; + test("hu", "ClearSpeak", expr, "minden x az egész számok halmaza melyekre teljesül, hogy 2 kisebb, mint x kisebb, mint 7")?; return Ok(()); } @@ -302,7 +302,7 @@ fn complicated_set_with_mtext() -> Result<()> { } "; test("hu", "ClearSpeak", expr, - "x a természetes számok halmaza melyekre teljesül, hogy minden x páros szám")?; + "minden x a természetes számok halmaza melyekre teljesül, hogy x páros szám")?; return Ok(()); } @@ -323,7 +323,7 @@ fn set_with_bar_member() -> Result<()> { } "; test_ClearSpeak("hu", "ClearSpeak_SetMemberSymbol", "Member", - expr, "x tagja az egész számok halmaza melyekre teljesül, hogy minden x nagyobb, mint 5")?; + expr, "minden x tagja az egész számok halmaza melyekre teljesül, hogy x nagyobb, mint 5")?; return Ok(()); } @@ -375,7 +375,7 @@ fn set_with_bar_element() -> Result<()> { } "; test_ClearSpeak("hu", "ClearSpeak_SetMemberSymbol", "Element", - expr, "x eleme ennek:, az egész számok halmaza melyekre teljesül, hogy minden x nagyobb, mint 5")?; + expr, "minden x eleme ennek:, az egész számok halmaza melyekre teljesül, hogy x nagyobb, mint 5")?; return Ok(()); } @@ -426,7 +426,7 @@ fn set_with_bar_in() -> Result<()> { } "; test_ClearSpeak("hu", "ClearSpeak_SetMemberSymbol", "In", - expr, "x az egész számok halmaza melyekre teljesül, hogy minden x nagyobb, mint 5")?; + expr, "minden x az egész számok halmaza melyekre teljesül, hogy x nagyobb, mint 5")?; return Ok(()); } @@ -477,7 +477,7 @@ fn set_with_bar_belongs() -> Result<()> { } "; test_ClearSpeak("hu", "ClearSpeak_SetMemberSymbol", "Belongs", - expr, "x hozzátartozó eleme ennek:, az egész számok halmaza melyekre teljesül, hogy minden x nagyobb, mint 5")?; + expr, "minden x hozzátartozó eleme ennek:, az egész számok halmaza melyekre teljesül, hogy x nagyobb, mint 5")?; return Ok(()); } @@ -563,7 +563,7 @@ fn silent_bracket() -> Result<()> { {x|x>2} "; test_ClearSpeak("hu", "ClearSpeak_Sets", "SilentBracket", expr, - "x melyekre teljesül, hogy minden x nagyobb, mint 2")?; + "minden x melyekre teljesül, hogy x nagyobb, mint 2")?; return Ok(()); } diff --git a/tests/Languages/hu/ClearSpeak/symbols_and_adornments.rs b/tests/Languages/hu/ClearSpeak/symbols_and_adornments.rs index ff38686c6..e137a3dd3 100644 --- a/tests/Languages/hu/ClearSpeak/symbols_and_adornments.rs +++ b/tests/Languages/hu/ClearSpeak/symbols_and_adornments.rs @@ -233,7 +233,7 @@ fn vertical_line_set() -> Result<()> { } "; test_ClearSpeak("hu", "ClearSpeak_VerticalLine", "Auto", expr, - "x melyekre teljesül, hogy minden x nagyobb, mint 0")?; + "minden x melyekre teljesül, hogy x nagyobb, mint 0")?; return Ok(()); } @@ -254,7 +254,7 @@ fn vertical_line_set_such_that() -> Result<()> { } "; test_ClearSpeak("hu", "ClearSpeak_VerticalLine", "SuchThat", expr, - "x melyekre teljesül, hogy minden x nagyobb, mint 0")?; + "minden x melyekre teljesül, hogy x nagyobb, mint 0")?; return Ok(()); } @@ -275,7 +275,7 @@ fn vertical_line_set_given() -> Result<()> { "; // the rules for set will override all the options -- ClearSpeak spec should be clarified test_ClearSpeak("hu", "ClearSpeak_VerticalLine", "Given", expr, - "x melyekre teljesül, hogy minden x nagyobb, mint 0")?; + "minden x melyekre teljesül, hogy x nagyobb, mint 0")?; return Ok(()); } @@ -299,7 +299,7 @@ fn vertical_line_set_and_abs() -> Result<()> { } "; test_ClearSpeak("hu", "ClearSpeak_VerticalLine", "Auto", expr, - "x melyekre teljesül, hogy minden x abszolút értéke; nagyobb, mint 2")?; + "minden x melyekre teljesül, hogy x abszolút értéke; nagyobb, mint 2")?; return Ok(()); } diff --git a/tests/Languages/hu/definitions.rs b/tests/Languages/hu/definitions.rs index 542f41885..62f910184 100644 --- a/tests/Languages/hu/definitions.rs +++ b/tests/Languages/hu/definitions.rs @@ -32,7 +32,7 @@ fn my_set_basic() -> Result<()> { "#; - test("hu", "ClearSpeak", expr, "az üres halmaz")?; + test("hu", "ClearSpeak", expr, "a halmaz x vessző, y")?; Ok(()) } @@ -91,7 +91,7 @@ fn set_difference_basic() -> Result<()> { "#; - test( "hu", "ClearSpeak", expr, "különbséghalmaz nagy a vessző, nagy b")?; + test( "hu", "ClearSpeak", expr, "különbséghalmaz nagy a és nagy b")?; Ok(()) } @@ -107,7 +107,7 @@ fn postfix_test() -> Result<()> { T "#, - "transzponált x", + "x transzponált", ), ( "highlight", @@ -205,9 +205,9 @@ fn functions_and_inverses_tests() -> Result<()> { //("fraction", "fraction x over y end fraction"), ("mixed-fraction", "x és y"), - ("quotient", "egész rész osztva x vessző, y"), + ("quotient", "egész rész x osztva y"), ("evaluated-at", "x értékelve y"), - ("remainder", "a maradék, osztva x vessző, y"), + ("remainder", "a maradék x osztva y"), ("max", "max x vessző, y vessző, z"), ("min", "min x vessző, y vessző, z"), @@ -223,13 +223,13 @@ fn functions_and_inverses_tests() -> Result<()> { ("complex-conjugate", "Komplex konjugált x"), ("complex-arg", "argumentum x"), - ("real-part", "valós rész"), - ("imaginary-part", "képzetes rész"), + ("real-part", "az igazi rész x"), + ("imaginary-part", "a képzeletbeli rész x"), ("polar-coordinate", "poláris koordináta x vessző, y"), ("spherical-coordinate", "gömb koordináta x vessző, y vessző, z"), ("cartesian-coordinate", "derékszögű koordináta x vessző, y vessző, z"), - ("coordinate", "pont, x vessző y vessző z"), + ("coordinate", "koordináta x vessző, y vessző, z"), ("floor", "lépcső x"), ("ceiling", "mennyezet x"), @@ -528,7 +528,7 @@ fn statistics_and_probability_tests() -> Result<()> { ("mode", "mód x"), // conditional probability typically two arguments - // ("conditional-probability", "probability of x given y"), + // ("conditional-probability", "valószínűség x adott y"), ]; for (intent, expected) in tests { @@ -576,7 +576,7 @@ fn linear_algebra_tests() -> Result<()> { ("span", "fesztávolság x"), // transpose supports both postfix and function; we test function explicitly - ("transpose", "transzponált x"), + ("transpose", "x transzponált"), // dimensional product is infix ("dimensional-product", "x a(z) y"), @@ -727,13 +727,13 @@ fn general_concepts_tests() -> Result<()> { ("permutation-cycle", "permutációs ciklus x"), // Binary structural / infix-style - // ("ordered-pair", "the pair of x and y"), + ("ordered-pair", "a pár x és y"), ("rate", "x per y"), ("binomial-coefficient", "x választ y"), ("embellished-name", "x megjegyzéssel y"), ("indexed-by", "x alsó index y"), - // ("translation", "translation by x comma, y"), // Changes translation to comma + ("translation", "fordítás x vessző, y"), // Changes translation to comma ("constraint", "x feltétellel y"), ]; diff --git a/tests/Languages/hu/shared.rs b/tests/Languages/hu/shared.rs index 23439e489..087a203a5 100644 --- a/tests/Languages/hu/shared.rs +++ b/tests/Languages/hu/shared.rs @@ -22,7 +22,7 @@ fn modified_vars() -> Result<()> { t → "; test("hu", "SimpleSpeak", expr, - "a súlyozott, b tilde, c rövid, b ellenőrzés, c súlyozott; plusz, r hacsek karakter mint repülő ékezet; plusz; x pont, y pont, z dupla pont, u hármas pont, v négyes pont; plusz x kalap, plusz vektor kezdete t")?; + "a súlyozott, b tilde, c rövid, b ellenőrzés, c súlyozott; plusz, r hacsek karakter mint repülő ékezet, plusz; x pont, y pont, z dupla pont, u hármas pont, v négyes pont; plusz x kalap, plusz vektor kezdete t")?; return Ok(()); } @@ -483,7 +483,7 @@ fn literal_speak_perpendicular() -> Result<()> { "#; - test("hu", "LiteralSpeak", expr, "nagy a jobbra nyíl; merőleges nagy b jobbra nyíl")?; + test("hu", "LiteralSpeak", expr, "nagy a jobbra nyíl, merőleges nagy b jobbra nyíl")?; return Ok(()); } @@ -569,7 +569,7 @@ fn literal_intent_property() -> Result<()> { "#; - test("hu", "SimpleSpeak", expr, "nagy a jobbra nyíl; merőleges nagy b jobbra nyíl")?; + test("hu", "SimpleSpeak", expr, "nagy a jobbra nyíl, merőleges nagy b jobbra nyíl")?; return Ok(()); } From adbf1e5bd0a4582b01d03b42314c008051580218 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 17 Aug 2026 14:16:39 -0700 Subject: [PATCH 039/120] move to release candidate --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b5b30304f..45de0ad1f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mathcat" -version = "0.7.6-beta.9" +version = "0.7.6-rc.1" authors = ["Neil Soiffer "] license = "MIT" description = "MathCAT: Math Capable Assistive Technology ('Speech and braille from MathML')" From 2267de346891b8829294b572be10d08c251bf0eb Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 17 Aug 2026 15:38:16 -0700 Subject: [PATCH 040/120] Added creation of minified yaml files for an alternative Rules.zip --- .github/workflows/rust.yml | 49 +++++++++++++++++++++++++++++++++--- AGENTS.md | 1 + PythonScripts/pyproject.toml | 3 ++- 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 5a9f85b4a..2fbdcbd22 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -46,12 +46,15 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 + - uses: astral-sh/setup-uv@v10.0.1 - name: Remove empty Rules directories run: find Rules -type d -empty -delete - name: Remove test directory run: rm -r Rules/Languages/zz - - name: create Rules.zip - run: zip -r Rules.zip Rules + - name: Create Rules.zip + run: uv run --project PythonScripts package-rules Rules Rules.zip + - name: Create Rules-minimized.zip + run: uv run --project PythonScripts package-rules Rules Rules-minimized.zip --minimize - name: Upload Rules.zip uses: actions/upload-artifact@v7 with: @@ -59,12 +62,45 @@ jobs: path: 'Rules.zip' compression-level: 0 retention-days: 1 + - name: Upload Rules-minimized.zip + uses: actions/upload-artifact@v7 + with: + name: 'Rules-minimized.zip' + path: 'Rules-minimized.zip' + compression-level: 0 + retention-days: 1 + + test-rules-package: + name: Test (${{ matrix.feature_name }}, ${{ matrix.rules_zip }}) + needs: zip-rules + strategy: + fail-fast: false + matrix: + rules_zip: [Rules.zip, Rules-minimized.zip] + include: + - feature_name: default + features: "" + - feature_name: no-unsafe + features: "--features no-unsafe" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Download ${{ matrix.rules_zip }} + uses: actions/download-artifact@v8 + with: + name: ${{ matrix.rules_zip }} + - name: Install packaged Rules + run: | + rm -rf Rules + unzip -q "${{ matrix.rules_zip }}" + - name: Run tests (${{ matrix.feature_name }}) + run: cargo test ${{ matrix.features }} --verbose pre-release: name: Pre Release continue-on-error: false if: github.event_name == 'push' && github.ref == 'refs/heads/main' # otherwise, job fails on pull requests, as GITHUB_TOKEN is not available - needs: [build, clippy, zip-rules] + needs: [build, clippy, zip-rules, test-rules-package] runs-on: ubuntu-latest permissions: contents: write @@ -73,6 +109,10 @@ jobs: uses: actions/download-artifact@v8 with: name: Rules.zip + - name: download Rules-minimized.zip + uses: actions/download-artifact@v8 + with: + name: Rules-minimized.zip # Moving 'latest' prerelease (replaces marvinpinto/action-automatic-releases) - name: Development Build release env: @@ -90,4 +130,5 @@ jobs: --latest=false \ --title "Development Build" \ --notes "Development build from ${SHA}" \ - Rules.zip + Rules.zip \ + Rules-minimized.zip diff --git a/AGENTS.md b/AGENTS.md index 0696e6979..6cb6f5381 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,6 +13,7 @@ but add common mistakes of AI agents here instead. - `ClearSpeak_Rules.yaml`, `SimpleSpeak_Rules.yaml` - `SharedRules/`, `unicode.yaml`, `unicode-full.yaml`, `definitions.yaml`, `navigate.yaml` - `build.rs` can bundle rules into `rules.zip` when `include-zip` is enabled. +- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style `Languages/**/unicode*.yaml` only) via `uv run --project PythonScripts package-rules Rules [--minimize]`. ## Translation Conventions - `t:` means untranslated or unverified. diff --git a/PythonScripts/pyproject.toml b/PythonScripts/pyproject.toml index 7b9adf68d..8f754577c 100644 --- a/PythonScripts/pyproject.toml +++ b/PythonScripts/pyproject.toml @@ -22,6 +22,7 @@ dependencies = [ [project.scripts] audit-translations = "audit_translations.cli:main" +package-rules = "rules_packaging.cli:main" [dependency-groups] dev = [ @@ -37,7 +38,7 @@ build-backend = "uv_build" exclude-newer = "1 week" [tool.uv.build-backend] -module-name = "audit_translations" +module-name = ["audit_translations", "rules_packaging"] module-root = "" [tool.ruff] From 487ce7102fbc409a9819fd2316cce03176ac7441 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 17 Aug 2026 15:41:28 -0700 Subject: [PATCH 041/120] fix build problem --- PythonScripts/pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PythonScripts/pyproject.toml b/PythonScripts/pyproject.toml index 8f754577c..dced0ef1f 100644 --- a/PythonScripts/pyproject.toml +++ b/PythonScripts/pyproject.toml @@ -22,7 +22,7 @@ dependencies = [ [project.scripts] audit-translations = "audit_translations.cli:main" -package-rules = "rules_packaging.cli:main" +package-rules = "audit_translations.rules_packaging.cli:main" [dependency-groups] dev = [ @@ -31,14 +31,14 @@ dev = [ ] [build-system] -requires = ["uv_build>=0.9.25,<0.10.0"] +requires = ["uv_build>=0.9.25,<0.13"] build-backend = "uv_build" [tool.uv] exclude-newer = "1 week" [tool.uv.build-backend] -module-name = ["audit_translations", "rules_packaging"] +module-name = "audit_translations" module-root = "" [tool.ruff] From 65250fc7852a40be50bbf425bcc6aae46bcff7d7 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 17 Aug 2026 15:50:13 -0700 Subject: [PATCH 042/120] try again for creation of minified yaml files for an alternative Rules.zip --- .github/workflows/rust.yml | 4 ++-- AGENTS.md | 2 +- PythonScripts/pyproject.toml | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2fbdcbd22..92f53f247 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -52,9 +52,9 @@ jobs: - name: Remove test directory run: rm -r Rules/Languages/zz - name: Create Rules.zip - run: uv run --project PythonScripts package-rules Rules Rules.zip + run: uv run PythonScripts/package_rules.py Rules Rules.zip - name: Create Rules-minimized.zip - run: uv run --project PythonScripts package-rules Rules Rules-minimized.zip --minimize + run: uv run PythonScripts/package_rules.py Rules Rules-minimized.zip --minimize - name: Upload Rules.zip uses: actions/upload-artifact@v7 with: diff --git a/AGENTS.md b/AGENTS.md index 6cb6f5381..270ebd606 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,7 @@ but add common mistakes of AI agents here instead. - `ClearSpeak_Rules.yaml`, `SimpleSpeak_Rules.yaml` - `SharedRules/`, `unicode.yaml`, `unicode-full.yaml`, `definitions.yaml`, `navigate.yaml` - `build.rs` can bundle rules into `rules.zip` when `include-zip` is enabled. -- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style `Languages/**/unicode*.yaml` only) via `uv run --project PythonScripts package-rules Rules [--minimize]`. +- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style `Languages/**/unicode*.yaml` only) via `uv run PythonScripts/package_rules.py Rules [--minimize]`. ## Translation Conventions - `t:` means untranslated or unverified. diff --git a/PythonScripts/pyproject.toml b/PythonScripts/pyproject.toml index dced0ef1f..7b9adf68d 100644 --- a/PythonScripts/pyproject.toml +++ b/PythonScripts/pyproject.toml @@ -22,7 +22,6 @@ dependencies = [ [project.scripts] audit-translations = "audit_translations.cli:main" -package-rules = "audit_translations.rules_packaging.cli:main" [dependency-groups] dev = [ @@ -31,7 +30,7 @@ dev = [ ] [build-system] -requires = ["uv_build>=0.9.25,<0.13"] +requires = ["uv_build>=0.9.25,<0.10.0"] build-backend = "uv_build" [tool.uv] From 0bac005d9cc07e734e7b0b29264280fd7a760f0b Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 17 Aug 2026 16:00:34 -0700 Subject: [PATCH 043/120] try again -- needs to run on linux --- .github/workflows/rust.yml | 4 ++-- AGENTS.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 92f53f247..8b3d6600b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -52,9 +52,9 @@ jobs: - name: Remove test directory run: rm -r Rules/Languages/zz - name: Create Rules.zip - run: uv run PythonScripts/package_rules.py Rules Rules.zip + run: uv run --script PythonScripts/package_rules.py Rules Rules.zip - name: Create Rules-minimized.zip - run: uv run PythonScripts/package_rules.py Rules Rules-minimized.zip --minimize + run: uv run --script PythonScripts/package_rules.py Rules Rules-minimized.zip --minimize - name: Upload Rules.zip uses: actions/upload-artifact@v7 with: diff --git a/AGENTS.md b/AGENTS.md index 270ebd606..a5632ced5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,7 @@ but add common mistakes of AI agents here instead. - `ClearSpeak_Rules.yaml`, `SimpleSpeak_Rules.yaml` - `SharedRules/`, `unicode.yaml`, `unicode-full.yaml`, `definitions.yaml`, `navigate.yaml` - `build.rs` can bundle rules into `rules.zip` when `include-zip` is enabled. -- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style `Languages/**/unicode*.yaml` only) via `uv run PythonScripts/package_rules.py Rules [--minimize]`. +- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style `Languages/**/unicode*.yaml` only) via `uv run --script PythonScripts/package_rules.py Rules [--minimize]`. ## Translation Conventions - `t:` means untranslated or unverified. From 80b749aec6bbb6aa453a8f7c354ee8426751755d Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 17 Aug 2026 16:13:25 -0700 Subject: [PATCH 044/120] Add standalone Rules packaging script --- PythonScripts/package_rules.py | 50 ++++++++ PythonScripts/rules_packaging/__init__.py | 5 + PythonScripts/rules_packaging/minimize.py | 108 ++++++++++++++++++ PythonScripts/rules_packaging/package.py | 47 ++++++++ .../rules_packaging/tests/__init__.py | 1 + .../rules_packaging/tests/test_package.py | 101 ++++++++++++++++ 6 files changed, 312 insertions(+) create mode 100644 PythonScripts/package_rules.py create mode 100644 PythonScripts/rules_packaging/__init__.py create mode 100644 PythonScripts/rules_packaging/minimize.py create mode 100644 PythonScripts/rules_packaging/package.py create mode 100644 PythonScripts/rules_packaging/tests/__init__.py create mode 100644 PythonScripts/rules_packaging/tests/test_package.py diff --git a/PythonScripts/package_rules.py b/PythonScripts/package_rules.py new file mode 100644 index 000000000..905a7bc90 --- /dev/null +++ b/PythonScripts/package_rules.py @@ -0,0 +1,50 @@ +# /// script +# requires-python = ">=3.14" +# dependencies = ["pyyaml"] +# /// +"""Package MathCAT Rules/ into Rules.zip or Rules-minimized.zip.""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +# Standalone script: sibling package, not installed via audit_translations. +sys.path.insert(0, str(Path(__file__).resolve().parent)) + +from rules_packaging.package import package_rules # noqa: E402 + + +def main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser( + description="Package MathCAT Rules/ into a zip archive. " + "Use --minimize for flow-style unicode.yaml / unicode-full.yaml without comments." + ) + parser.add_argument("source", type=Path, help="Path to the Rules directory") + parser.add_argument("output", type=Path, help="Output zip file path (e.g. Rules.zip)") + parser.add_argument( + "--minimize", + action="store_true", + help="Emit compact flow-style YAML (comments and block formatting removed)", + ) + args = parser.parse_args(argv) + + try: + stats = package_rules(args.source, args.output, minimize=args.minimize) + except (FileNotFoundError, ValueError) as exc: + print(f"error: {exc}", file=sys.stderr) + return 1 + + label = "minimized" if args.minimize else "standard" + print(f"Created {label} archive: {args.output}") + if args.minimize: + print(f" minimized YAML files: {stats['minimized_yaml_files']}") + print(f" copied verbatim: {stats['copied_files']}") + for warning in stats["warnings"]: + print(f" warning: {warning}", file=sys.stderr) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/PythonScripts/rules_packaging/__init__.py b/PythonScripts/rules_packaging/__init__.py new file mode 100644 index 000000000..07c96c9d2 --- /dev/null +++ b/PythonScripts/rules_packaging/__init__.py @@ -0,0 +1,5 @@ +"""Standalone Rules zip packaging and YAML minimization (not part of audit_translations).""" + +from .package import package_rules + +__all__ = ["package_rules"] diff --git a/PythonScripts/rules_packaging/minimize.py b/PythonScripts/rules_packaging/minimize.py new file mode 100644 index 000000000..7fb6a05d5 --- /dev/null +++ b/PythonScripts/rules_packaging/minimize.py @@ -0,0 +1,108 @@ +"""Convert MathCAT Rules YAML files to compact flow-style form.""" + +from __future__ import annotations + +import re +from pathlib import Path +from typing import Any + +import yaml + +YAML_SUFFIXES = {".yaml", ".yml"} +MINIMIZE_NAMES = {"unicode.yaml", "unicode-full.yaml"} + + +def should_minimize(path: Path) -> bool: + if path.suffix.lower() not in YAML_SUFFIXES or path.name not in MINIMIZE_NAMES: + return False + # Speech unicode only — braille unicode.yaml keeps block form (tests and edits rely on it). + return "Languages" in path.parts + + +def _configure_yaml_dumper() -> None: + """Quote all strings so yaml-rust does not treat words like 'infinity' as floats.""" + + def represent_str(dumper: yaml.SafeDumper, data: str) -> yaml.nodes.ScalarNode: + return dumper.represent_scalar("tag:yaml.org,2002:str", data, style='"') + + yaml.add_representer(str, represent_str, Dumper=yaml.SafeDumper) + + +_configure_yaml_dumper() + + +def preprocess_for_load(text: str) -> str: + """Normalize MathCAT YAML quirks so PyYAML can parse it.""" + text = text.replace("\t", " ") + # Bare double-quote or colon used as a mapping key in unicode list entries. + text = re.sub(r'(?m)^(\s*-\s*)":(\s*:\s*)', r'\1"\\"":\2', text) + text = re.sub(r'(?m)^(\s*-\s*)\\"(\s*:\s*)', r'\1"\\""\2', text) + return text + + +def load_yaml(text: str) -> Any: + """Load YAML using PyYAML with MathCAT-specific normalizations.""" + candidates = (text, text.replace("\t", " "), preprocess_for_load(text)) + last_error: yaml.YAMLError | None = None + for candidate in candidates: + try: + return yaml.safe_load(candidate) + except yaml.YAMLError as exc: + last_error = exc + if last_error is not None: + raise last_error + return None + + +def dump_flow_style(data: Any) -> str: + """Emit minimized flow-style YAML without comments.""" + if data is None: + return "" + dumped = yaml.dump( + data, + allow_unicode=True, + default_flow_style=True, + width=10**9, + sort_keys=False, + Dumper=yaml.SafeDumper, + ) + return dumped if dumped.endswith("\n") else f"{dumped}\n" + + +def minimize_yaml_text(text: str) -> tuple[str, bool]: + """Return flow-style YAML and whether parsing succeeded.""" + try: + data = load_yaml(text) + except yaml.YAMLError: + return text, False + return dump_flow_style(data), True + + +def copy_rules_tree(source: Path, destination: Path, *, minimize: bool) -> tuple[int, int, list[str]]: + """Copy ``source`` Rules tree to ``destination``, optionally minimizing YAML.""" + minimized = 0 + copied = 0 + warnings: list[str] = [] + + for src_path in sorted(source.rglob("*")): + if src_path.is_dir(): + continue + rel = src_path.relative_to(source) + dest_path = destination / rel + dest_path.parent.mkdir(parents=True, exist_ok=True) + + if minimize and should_minimize(src_path): + text = src_path.read_text(encoding="utf-8") + out, ok = minimize_yaml_text(text) + if ok: + dest_path.write_text(out, encoding="utf-8", newline="\n") + minimized += 1 + else: + dest_path.write_bytes(src_path.read_bytes()) + warnings.append(f"could not minimize {rel}; copied verbatim") + copied += 1 + else: + dest_path.write_bytes(src_path.read_bytes()) + copied += 1 + + return minimized, copied, warnings diff --git a/PythonScripts/rules_packaging/package.py b/PythonScripts/rules_packaging/package.py new file mode 100644 index 000000000..9b3c47f9d --- /dev/null +++ b/PythonScripts/rules_packaging/package.py @@ -0,0 +1,47 @@ +"""Create Rules.zip archives from a Rules directory tree.""" + +from __future__ import annotations + +import shutil +import tempfile +import zipfile +from pathlib import Path + +from .minimize import copy_rules_tree + + +def zip_directory(source_dir: Path, zip_path: Path) -> None: + """Write ``source_dir`` contents into ``zip_path`` preserving relative paths.""" + zip_path.parent.mkdir(parents=True, exist_ok=True) + with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive: + for path in sorted(source_dir.rglob("*")): + if path.is_file(): + archive.write(path, path.relative_to(source_dir.parent).as_posix()) + + +def package_rules(source: Path, output: Path, *, minimize: bool = False) -> dict[str, int | list[str]]: + """Package ``source`` (Rules directory) into ``output`` zip file.""" + source = source.resolve() + output = output.resolve() + + if not source.is_dir(): + raise FileNotFoundError(f"Rules source directory not found: {source}") + if source.name != "Rules": + raise ValueError(f"Expected a directory named 'Rules', got '{source.name}'") + + with tempfile.TemporaryDirectory(prefix="mathcat-rules-") as tmp: + staging_root = Path(tmp) + staged_rules = staging_root / "Rules" + if minimize: + minimized, copied, warnings = copy_rules_tree(source, staged_rules, minimize=True) + else: + shutil.copytree(source, staged_rules) + minimized, copied, warnings = 0, sum(1 for p in staged_rules.rglob("*") if p.is_file()), [] + + zip_directory(staged_rules, output) + + return { + "minimized_yaml_files": minimized, + "copied_files": copied, + "warnings": warnings, + } diff --git a/PythonScripts/rules_packaging/tests/__init__.py b/PythonScripts/rules_packaging/tests/__init__.py new file mode 100644 index 000000000..b5d3c1aa2 --- /dev/null +++ b/PythonScripts/rules_packaging/tests/__init__.py @@ -0,0 +1 @@ +"""Tests for rules packaging.""" diff --git a/PythonScripts/rules_packaging/tests/test_package.py b/PythonScripts/rules_packaging/tests/test_package.py new file mode 100644 index 000000000..a8364c93a --- /dev/null +++ b/PythonScripts/rules_packaging/tests/test_package.py @@ -0,0 +1,101 @@ +"""Tests for Rules packaging and YAML minimization.""" + +from __future__ import annotations + +import zipfile +from pathlib import Path + +import yaml + +from rules_packaging.minimize import dump_flow_style, load_yaml, minimize_yaml_text +from rules_packaging.package import package_rules + + +def test_minimize_yaml_strips_comments_and_uses_flow_style() -> None: + """Minimized YAML drops comments and uses flow-style collections.""" + source = """--- +# comment + - \"+\": [t: \"plus\"] # inline +""" + minimized, ok = minimize_yaml_text(source) + assert ok + assert "#" not in minimized + assert "[" in minimized and "{" in minimized or "t:" in minimized + assert load_yaml(minimized) == load_yaml(source) + + +def test_package_rules_standard_zip_layout(tmp_path: Path) -> None: + """Standard packaging preserves Rules/ prefix inside the zip.""" + rules = tmp_path / "Rules" + rules.mkdir() + (rules / "prefs.yaml").write_text("- SpeechStyle: [t: \"ClearSpeak\"]\n", encoding="utf-8") + + output = tmp_path / "Rules.zip" + package_rules(rules, output, minimize=False) + + with zipfile.ZipFile(output) as archive: + names = archive.namelist() + assert "Rules/prefs.yaml" in names + + +def test_dump_flow_style_quotes_infinity() -> None: + """Flow-style dump must quote 'infinity' so yaml-rust does not read it as a float.""" + minimized = dump_flow_style([{"∞": [{"t": "infinity"}]}]) + assert '"infinity"' in minimized + assert "t: infinity" not in minimized + + +def test_should_not_minimize_braille_unicode(tmp_path: Path) -> None: + """Braille unicode.yaml is copied verbatim even when minimization is enabled.""" + rules = tmp_path / "Rules" + braille = rules / "Braille" / "Nemeth" + braille.mkdir(parents=True) + source = """--- + - "1": [t: "N⠂"] +""" + (braille / "unicode.yaml").write_text(source, encoding="utf-8") + + output = tmp_path / "Rules-minimized.zip" + stats = package_rules(rules, output, minimize=True) + + assert stats["minimized_yaml_files"] == 0 + with zipfile.ZipFile(output) as archive: + text = archive.read("Rules/Braille/Nemeth/unicode.yaml").decode("utf-8") + assert text.replace("\r\n", "\n") == source.replace("\r\n", "\n") + + +def test_package_rules_minimized_smaller_than_standard(tmp_path: Path) -> None: + """Minimized archives use flow-style YAML and are not larger than standard.""" + rules = tmp_path / "Rules" + lang = rules / "Languages" / "en" + lang.mkdir(parents=True) + (lang / "unicode.yaml").write_text( + """--- +# header comment + - \"+\": [t: \"plus\"] # trailing + - \"-\": [t: \"minus\"] +""", + encoding="utf-8", + ) + (lang / "definitions.yaml").write_text( + """--- + - AdditionalFunctionNames: + - real=inf +""", + encoding="utf-8", + ) + + standard = tmp_path / "Rules.zip" + minimized = tmp_path / "Rules-minimized.zip" + package_rules(rules, standard, minimize=False) + stats = package_rules(rules, minimized, minimize=True) + + assert stats["minimized_yaml_files"] == 1 + assert minimized.stat().st_size <= standard.stat().st_size + + with zipfile.ZipFile(minimized) as archive: + unicode_text = archive.read("Rules/Languages/en/unicode.yaml").decode("utf-8") + defs_text = archive.read("Rules/Languages/en/definitions.yaml").decode("utf-8") + assert "#" not in unicode_text + assert "real=inf" in defs_text + assert yaml.safe_load(unicode_text) is not None From b1f15e94c7b6a79ee77b62eeed773ee7605bdb90 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 17 Aug 2026 16:13:53 -0700 Subject: [PATCH 045/120] try again with more files in github --- .github/workflows/rust.yml | 4 ++-- AGENTS.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8b3d6600b..8183f9029 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -52,9 +52,9 @@ jobs: - name: Remove test directory run: rm -r Rules/Languages/zz - name: Create Rules.zip - run: uv run --script PythonScripts/package_rules.py Rules Rules.zip + run: uv run --no-project --script PythonScripts/package_rules.py Rules Rules.zip - name: Create Rules-minimized.zip - run: uv run --script PythonScripts/package_rules.py Rules Rules-minimized.zip --minimize + run: uv run --no-project --script PythonScripts/package_rules.py Rules Rules-minimized.zip --minimize - name: Upload Rules.zip uses: actions/upload-artifact@v7 with: diff --git a/AGENTS.md b/AGENTS.md index a5632ced5..f3b6670c4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,7 @@ but add common mistakes of AI agents here instead. - `ClearSpeak_Rules.yaml`, `SimpleSpeak_Rules.yaml` - `SharedRules/`, `unicode.yaml`, `unicode-full.yaml`, `definitions.yaml`, `navigate.yaml` - `build.rs` can bundle rules into `rules.zip` when `include-zip` is enabled. -- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style `Languages/**/unicode*.yaml` only) via `uv run --script PythonScripts/package_rules.py Rules [--minimize]`. +- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style `Languages/**/unicode*.yaml` only) via `uv run --no-project --script PythonScripts/package_rules.py Rules [--minimize]`. ## Translation Conventions - `t:` means untranslated or unverified. From 43af5610615a29e061386c3ec4d63cdd23dac023 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 17 Aug 2026 16:40:58 -0700 Subject: [PATCH 046/120] try again adding 'zz' test dir for testing --- .github/workflows/rust.yml | 2 ++ AGENTS.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8183f9029..8b60e93a5 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -93,6 +93,8 @@ jobs: run: | rm -rf Rules unzip -q "${{ matrix.rules_zip }}" + # Release zips omit the synthetic zz test language; restore it for unit tests only. + git checkout HEAD -- Rules/Languages/zz - name: Run tests (${{ matrix.feature_name }}) run: cargo test ${{ matrix.features }} --verbose diff --git a/AGENTS.md b/AGENTS.md index f3b6670c4..d9ed0186b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,7 @@ but add common mistakes of AI agents here instead. - `ClearSpeak_Rules.yaml`, `SimpleSpeak_Rules.yaml` - `SharedRules/`, `unicode.yaml`, `unicode-full.yaml`, `definitions.yaml`, `navigate.yaml` - `build.rs` can bundle rules into `rules.zip` when `include-zip` is enabled. -- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style `Languages/**/unicode*.yaml` only) via `uv run --no-project --script PythonScripts/package_rules.py Rules [--minimize]`. +- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style `Languages/**/unicode*.yaml` only) via `uv run --no-project --script PythonScripts/package_rules.py Rules [--minimize]`. Both omit `Languages/zz`; the `test-rules-package` job restores `zz` from git after unzip so unit tests still run against release-like archives. ## Translation Conventions - `t:` means untranslated or unverified. From 82e81889ee56ca43c967f72cb37075de60e186a2 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 17 Aug 2026 16:50:24 -0700 Subject: [PATCH 047/120] internally zip of the Language/Braille dirs --- AGENTS.md | 2 +- PythonScripts/rules_packaging/package.py | 2 ++ .../rules_packaging/tests/test_package.py | 31 ++++++++++++++----- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index d9ed0186b..e4076ece7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,7 @@ but add common mistakes of AI agents here instead. - `ClearSpeak_Rules.yaml`, `SimpleSpeak_Rules.yaml` - `SharedRules/`, `unicode.yaml`, `unicode-full.yaml`, `definitions.yaml`, `navigate.yaml` - `build.rs` can bundle rules into `rules.zip` when `include-zip` is enabled. -- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style `Languages/**/unicode*.yaml` only) via `uv run --no-project --script PythonScripts/package_rules.py Rules [--minimize]`. Both omit `Languages/zz`; the `test-rules-package` job restores `zz` from git after unzip so unit tests still run against release-like archives. +- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style `Languages/**/unicode*.yaml` only) via `uv run --no-project --script PythonScripts/package_rules.py Rules [--minimize]`. Each language/braille subdir is stored as `/.zip` (matching `build.rs`); top-level and `Intent/` YAML stay loose. Both omit `Languages/zz`; the `test-rules-package` job restores `zz` from git after unzip so unit tests still run against release-like archives. ## Translation Conventions - `t:` means untranslated or unverified. diff --git a/PythonScripts/rules_packaging/package.py b/PythonScripts/rules_packaging/package.py index 9b3c47f9d..214ac0db5 100644 --- a/PythonScripts/rules_packaging/package.py +++ b/PythonScripts/rules_packaging/package.py @@ -8,6 +8,7 @@ from pathlib import Path from .minimize import copy_rules_tree +from .release_layout import apply_release_layout def zip_directory(source_dir: Path, zip_path: Path) -> None: @@ -38,6 +39,7 @@ def package_rules(source: Path, output: Path, *, minimize: bool = False) -> dict shutil.copytree(source, staged_rules) minimized, copied, warnings = 0, sum(1 for p in staged_rules.rglob("*") if p.is_file()), [] + apply_release_layout(staged_rules) zip_directory(staged_rules, output) return { diff --git a/PythonScripts/rules_packaging/tests/test_package.py b/PythonScripts/rules_packaging/tests/test_package.py index a8364c93a..c2adc11f7 100644 --- a/PythonScripts/rules_packaging/tests/test_package.py +++ b/PythonScripts/rules_packaging/tests/test_package.py @@ -2,6 +2,7 @@ from __future__ import annotations +import io import zipfile from pathlib import Path @@ -11,6 +12,12 @@ from rules_packaging.package import package_rules +def read_nested_zip_text(archive: zipfile.ZipFile, outer_path: str, inner_path: str) -> str: + """Read ``inner_path`` from a zip file stored inside the outer archive.""" + with zipfile.ZipFile(io.BytesIO(archive.read(outer_path))) as inner: + return inner.read(inner_path).decode("utf-8") + + def test_minimize_yaml_strips_comments_and_uses_flow_style() -> None: """Minimized YAML drops comments and uses flow-style collections.""" source = """--- @@ -25,17 +32,22 @@ def test_minimize_yaml_strips_comments_and_uses_flow_style() -> None: def test_package_rules_standard_zip_layout(tmp_path: Path) -> None: - """Standard packaging preserves Rules/ prefix inside the zip.""" + """Standard packaging preserves Rules/ prefix and per-language inner zips.""" rules = tmp_path / "Rules" - rules.mkdir() + lang = rules / "Languages" / "en" + lang.mkdir(parents=True) (rules / "prefs.yaml").write_text("- SpeechStyle: [t: \"ClearSpeak\"]\n", encoding="utf-8") + (lang / "unicode.yaml").write_text("- \"+\": [t: \"plus\"]\n", encoding="utf-8") output = tmp_path / "Rules.zip" package_rules(rules, output, minimize=False) with zipfile.ZipFile(output) as archive: names = archive.namelist() - assert "Rules/prefs.yaml" in names + assert "Rules/prefs.yaml" in names + assert "Rules/Languages/en/en.zip" in names + assert "Rules/Languages/en/unicode.yaml" not in names + assert read_nested_zip_text(archive, "Rules/Languages/en/en.zip", "unicode.yaml").replace("\r\n", "\n") == "- \"+\": [t: \"plus\"]\n" def test_dump_flow_style_quotes_infinity() -> None: @@ -60,12 +72,12 @@ def test_should_not_minimize_braille_unicode(tmp_path: Path) -> None: assert stats["minimized_yaml_files"] == 0 with zipfile.ZipFile(output) as archive: - text = archive.read("Rules/Braille/Nemeth/unicode.yaml").decode("utf-8") + text = read_nested_zip_text(archive, "Rules/Braille/Nemeth/Nemeth.zip", "unicode.yaml") assert text.replace("\r\n", "\n") == source.replace("\r\n", "\n") -def test_package_rules_minimized_smaller_than_standard(tmp_path: Path) -> None: - """Minimized archives use flow-style YAML and are not larger than standard.""" +def test_package_rules_minimized_matches_release_layout(tmp_path: Path) -> None: + """Minimized archives use inner language zips and flow-style unicode YAML.""" rules = tmp_path / "Rules" lang = rules / "Languages" / "en" lang.mkdir(parents=True) @@ -94,8 +106,11 @@ def test_package_rules_minimized_smaller_than_standard(tmp_path: Path) -> None: assert minimized.stat().st_size <= standard.stat().st_size with zipfile.ZipFile(minimized) as archive: - unicode_text = archive.read("Rules/Languages/en/unicode.yaml").decode("utf-8") - defs_text = archive.read("Rules/Languages/en/definitions.yaml").decode("utf-8") + names = archive.namelist() + assert "Rules/Languages/en/en.zip" in names + assert "Rules/Languages/en/unicode.yaml" not in names + unicode_text = read_nested_zip_text(archive, "Rules/Languages/en/en.zip", "unicode.yaml") + defs_text = read_nested_zip_text(archive, "Rules/Languages/en/en.zip", "definitions.yaml") assert "#" not in unicode_text assert "real=inf" in defs_text assert yaml.safe_load(unicode_text) is not None From 082afd154adf410627932feab186171f25600bbc Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 17 Aug 2026 16:51:11 -0700 Subject: [PATCH 048/120] add another file for packaging the rules --- .../rules_packaging/release_layout.py | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 PythonScripts/rules_packaging/release_layout.py diff --git a/PythonScripts/rules_packaging/release_layout.py b/PythonScripts/rules_packaging/release_layout.py new file mode 100644 index 000000000..74deff496 --- /dev/null +++ b/PythonScripts/rules_packaging/release_layout.py @@ -0,0 +1,73 @@ +"""Transform a Rules tree into the release layout used by build.rs and CI.""" + +from __future__ import annotations + +import shutil +import zipfile +from pathlib import Path + +YAML_SUFFIXES = {".yaml", ".yml"} +SKIP_LANGUAGE_DIRS = {"zz"} +ZIP_SECTIONS = ("Languages", "Braille") + + +def apply_release_layout(rules_dir: Path) -> None: + """Zip each language/braille subdir into ``/.zip`` and drop loose YAML inside.""" + languages = rules_dir / "Languages" + zz = languages / "zz" + if zz.is_dir(): + shutil.rmtree(zz) + + for section in ZIP_SECTIONS: + section_dir = rules_dir / section + if section_dir.is_dir(): + _zip_immediate_subdirs(section_dir) + + +def _zip_immediate_subdirs(parent: Path) -> None: + for entry in sorted(parent.iterdir(), key=lambda path: path.name): + if not entry.is_dir(): + continue + if entry.name in SKIP_LANGUAGE_DIRS: + shutil.rmtree(entry) + continue + + inner_zip = entry / f"{entry.name}.zip" + yaml_count = _write_yaml_zip(entry, inner_zip) + if yaml_count == 0: + inner_zip.unlink(missing_ok=True) + shutil.rmtree(entry) + continue + + _remove_files_except(entry, keep={inner_zip}) + _prune_empty_dirs(entry) + + +def _write_yaml_zip(source_dir: Path, zip_path: Path) -> int: + """Write all YAML under ``source_dir`` into ``zip_path``; return file count.""" + yaml_files = [ + path + for path in sorted(source_dir.rglob("*")) + if path.is_file() and path.suffix.lower() in YAML_SUFFIXES and path != zip_path + ] + if not yaml_files: + return 0 + + with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive: + for path in yaml_files: + archive.write(path, path.relative_to(source_dir).as_posix()) + return len(yaml_files) + + +def _remove_files_except(root: Path, *, keep: set[Path]) -> None: + for path in sorted(root.rglob("*"), reverse=True): + if path.is_file() and path.resolve() not in {item.resolve() for item in keep}: + path.unlink() + + +def _prune_empty_dirs(root: Path) -> None: + for path in sorted((item for item in root.rglob("*") if item.is_dir()), reverse=True): + try: + path.rmdir() + except OSError: + continue From 61c04bd722ae61d42844b8d5c5d0092ba6b31637 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 17 Aug 2026 17:00:21 -0700 Subject: [PATCH 049/120] trying again with zipping -- AI is not doing a very good job... --- PythonScripts/rules_packaging/package.py | 4 ++-- PythonScripts/rules_packaging/release_layout.py | 5 +++-- PythonScripts/rules_packaging/tests/test_package.py | 3 +++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/PythonScripts/rules_packaging/package.py b/PythonScripts/rules_packaging/package.py index 214ac0db5..733662cb8 100644 --- a/PythonScripts/rules_packaging/package.py +++ b/PythonScripts/rules_packaging/package.py @@ -4,9 +4,9 @@ import shutil import tempfile -import zipfile from pathlib import Path +from .compress import open_rules_zip from .minimize import copy_rules_tree from .release_layout import apply_release_layout @@ -14,7 +14,7 @@ def zip_directory(source_dir: Path, zip_path: Path) -> None: """Write ``source_dir`` contents into ``zip_path`` preserving relative paths.""" zip_path.parent.mkdir(parents=True, exist_ok=True) - with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive: + with open_rules_zip(zip_path) as archive: for path in sorted(source_dir.rglob("*")): if path.is_file(): archive.write(path, path.relative_to(source_dir.parent).as_posix()) diff --git a/PythonScripts/rules_packaging/release_layout.py b/PythonScripts/rules_packaging/release_layout.py index 74deff496..792687ad6 100644 --- a/PythonScripts/rules_packaging/release_layout.py +++ b/PythonScripts/rules_packaging/release_layout.py @@ -3,9 +3,10 @@ from __future__ import annotations import shutil -import zipfile from pathlib import Path +from .compress import open_rules_zip + YAML_SUFFIXES = {".yaml", ".yml"} SKIP_LANGUAGE_DIRS = {"zz"} ZIP_SECTIONS = ("Languages", "Braille") @@ -53,7 +54,7 @@ def _write_yaml_zip(source_dir: Path, zip_path: Path) -> int: if not yaml_files: return 0 - with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive: + with open_rules_zip(zip_path) as archive: for path in yaml_files: archive.write(path, path.relative_to(source_dir).as_posix()) return len(yaml_files) diff --git a/PythonScripts/rules_packaging/tests/test_package.py b/PythonScripts/rules_packaging/tests/test_package.py index c2adc11f7..334c5b851 100644 --- a/PythonScripts/rules_packaging/tests/test_package.py +++ b/PythonScripts/rules_packaging/tests/test_package.py @@ -8,6 +8,7 @@ import yaml +from rules_packaging.compress import RULES_ZIP_COMPRESSION from rules_packaging.minimize import dump_flow_style, load_yaml, minimize_yaml_text from rules_packaging.package import package_rules @@ -47,6 +48,8 @@ def test_package_rules_standard_zip_layout(tmp_path: Path) -> None: assert "Rules/prefs.yaml" in names assert "Rules/Languages/en/en.zip" in names assert "Rules/Languages/en/unicode.yaml" not in names + with zipfile.ZipFile(io.BytesIO(archive.read("Rules/Languages/en/en.zip"))) as inner: + assert all(info.compress_type == RULES_ZIP_COMPRESSION for info in inner.infolist()) assert read_nested_zip_text(archive, "Rules/Languages/en/en.zip", "unicode.yaml").replace("\r\n", "\n") == "- \"+\": [t: \"plus\"]\n" From 917dbe5649da96ae9d2b95ffb59b282c199ec85d Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 17 Aug 2026 17:05:00 -0700 Subject: [PATCH 050/120] Merged the messy multiple files AI created into one file --- PythonScripts/package_rules.py | 221 +++++++++++++++++- PythonScripts/rules_packaging/__init__.py | 5 - PythonScripts/rules_packaging/minimize.py | 108 --------- PythonScripts/rules_packaging/package.py | 49 ---- .../rules_packaging/release_layout.py | 74 ------ .../rules_packaging/tests/__init__.py | 1 - .../test_package_rules.py} | 10 +- 7 files changed, 225 insertions(+), 243 deletions(-) delete mode 100644 PythonScripts/rules_packaging/__init__.py delete mode 100644 PythonScripts/rules_packaging/minimize.py delete mode 100644 PythonScripts/rules_packaging/package.py delete mode 100644 PythonScripts/rules_packaging/release_layout.py delete mode 100644 PythonScripts/rules_packaging/tests/__init__.py rename PythonScripts/{rules_packaging/tests/test_package.py => tests/test_package_rules.py} (95%) diff --git a/PythonScripts/package_rules.py b/PythonScripts/package_rules.py index 905a7bc90..53ff2d89e 100644 --- a/PythonScripts/package_rules.py +++ b/PythonScripts/package_rules.py @@ -7,13 +7,228 @@ from __future__ import annotations import argparse +import re +import shutil import sys +import tempfile +import zipfile from pathlib import Path +from typing import Any -# Standalone script: sibling package, not installed via audit_translations. -sys.path.insert(0, str(Path(__file__).resolve().parent)) +import yaml -from rules_packaging.package import package_rules # noqa: E402 +YAML_SUFFIXES = {".yaml", ".yml"} +MINIMIZE_NAMES = {"unicode.yaml", "unicode-full.yaml"} +SKIP_LANGUAGE_DIRS = {"zz"} +ZIP_SECTIONS = ("Languages", "Braille") + +# Non-WASM MathCAT links the `zip` crate with only the `bzip2` feature (see Cargo.toml). +# Inner language/braille zips must use BZIP2 — DEFLATE (method 8) fails at runtime with +# CompressionMethodNotSupported. build.rs uses the same choice for native targets. +RULES_ZIP_COMPRESSION = zipfile.ZIP_BZIP2 +RULES_ZIP_COMPRESSLEVEL = 9 + + +def _open_rules_zip(path: Path, mode: str = "w") -> zipfile.ZipFile: + return zipfile.ZipFile( + path, + mode, + compression=RULES_ZIP_COMPRESSION, + compresslevel=RULES_ZIP_COMPRESSLEVEL, + ) + + +def _configure_yaml_dumper() -> None: + """Quote all strings so yaml-rust does not treat words like 'infinity' as floats.""" + + def represent_str(dumper: yaml.SafeDumper, data: str) -> yaml.nodes.ScalarNode: + return dumper.represent_scalar("tag:yaml.org,2002:str", data, style='"') + + yaml.add_representer(str, represent_str, Dumper=yaml.SafeDumper) + + +_configure_yaml_dumper() + + +def should_minimize(path: Path) -> bool: + if path.suffix.lower() not in YAML_SUFFIXES or path.name not in MINIMIZE_NAMES: + return False + # Speech unicode only — braille unicode.yaml keeps block form (tests and edits rely on it). + return "Languages" in path.parts + + +def preprocess_for_load(text: str) -> str: + """Normalize MathCAT YAML quirks so PyYAML can parse it.""" + text = text.replace("\t", " ") + text = re.sub(r'(?m)^(\s*-\s*)":(\s*:\s*)', r'\1"\\"":\2', text) + text = re.sub(r'(?m)^(\s*-\s*)\\"(\s*:\s*)', r'\1"\\""\2', text) + return text + + +def load_yaml(text: str) -> Any: + """Load YAML using PyYAML with MathCAT-specific normalizations.""" + candidates = (text, text.replace("\t", " "), preprocess_for_load(text)) + last_error: yaml.YAMLError | None = None + for candidate in candidates: + try: + return yaml.safe_load(candidate) + except yaml.YAMLError as exc: + last_error = exc + if last_error is not None: + raise last_error + return None + + +def dump_flow_style(data: Any) -> str: + """Emit minimized flow-style YAML without comments.""" + if data is None: + return "" + dumped = yaml.dump( + data, + allow_unicode=True, + default_flow_style=True, + width=10**9, + sort_keys=False, + Dumper=yaml.SafeDumper, + ) + return dumped if dumped.endswith("\n") else f"{dumped}\n" + + +def minimize_yaml_text(text: str) -> tuple[str, bool]: + """Return flow-style YAML and whether parsing succeeded.""" + try: + data = load_yaml(text) + except yaml.YAMLError: + return text, False + return dump_flow_style(data), True + + +def copy_rules_tree(source: Path, destination: Path, *, minimize: bool) -> tuple[int, int, list[str]]: + """Copy ``source`` Rules tree to ``destination``, optionally minimizing YAML.""" + minimized = 0 + copied = 0 + warnings: list[str] = [] + + for src_path in sorted(source.rglob("*")): + if src_path.is_dir(): + continue + rel = src_path.relative_to(source) + dest_path = destination / rel + dest_path.parent.mkdir(parents=True, exist_ok=True) + + if minimize and should_minimize(src_path): + text = src_path.read_text(encoding="utf-8") + out, ok = minimize_yaml_text(text) + if ok: + dest_path.write_text(out, encoding="utf-8", newline="\n") + minimized += 1 + else: + dest_path.write_bytes(src_path.read_bytes()) + warnings.append(f"could not minimize {rel}; copied verbatim") + copied += 1 + else: + dest_path.write_bytes(src_path.read_bytes()) + copied += 1 + + return minimized, copied, warnings + + +def apply_release_layout(rules_dir: Path) -> None: + """Zip each language/braille subdir into ``/.zip`` and drop loose YAML inside.""" + zz = rules_dir / "Languages" / "zz" + if zz.is_dir(): + shutil.rmtree(zz) + + for section in ZIP_SECTIONS: + section_dir = rules_dir / section + if section_dir.is_dir(): + _zip_immediate_subdirs(section_dir) + + +def _zip_immediate_subdirs(parent: Path) -> None: + for entry in sorted(parent.iterdir(), key=lambda path: path.name): + if not entry.is_dir(): + continue + if entry.name in SKIP_LANGUAGE_DIRS: + shutil.rmtree(entry) + continue + + inner_zip = entry / f"{entry.name}.zip" + yaml_count = _write_yaml_zip(entry, inner_zip) + if yaml_count == 0: + inner_zip.unlink(missing_ok=True) + shutil.rmtree(entry) + continue + + _remove_files_except(entry, keep={inner_zip}) + _prune_empty_dirs(entry) + + +def _write_yaml_zip(source_dir: Path, zip_path: Path) -> int: + yaml_files = [ + path + for path in sorted(source_dir.rglob("*")) + if path.is_file() and path.suffix.lower() in YAML_SUFFIXES and path != zip_path + ] + if not yaml_files: + return 0 + + with _open_rules_zip(zip_path) as archive: + for path in yaml_files: + archive.write(path, path.relative_to(source_dir).as_posix()) + return len(yaml_files) + + +def _remove_files_except(root: Path, *, keep: set[Path]) -> None: + keep_resolved = {item.resolve() for item in keep} + for path in sorted(root.rglob("*"), reverse=True): + if path.is_file() and path.resolve() not in keep_resolved: + path.unlink() + + +def _prune_empty_dirs(root: Path) -> None: + for path in sorted((item for item in root.rglob("*") if item.is_dir()), reverse=True): + try: + path.rmdir() + except OSError: + continue + + +def zip_directory(source_dir: Path, zip_path: Path) -> None: + """Write ``source_dir`` contents into ``zip_path`` preserving relative paths.""" + zip_path.parent.mkdir(parents=True, exist_ok=True) + with _open_rules_zip(zip_path) as archive: + for path in sorted(source_dir.rglob("*")): + if path.is_file(): + archive.write(path, path.relative_to(source_dir.parent).as_posix()) + + +def package_rules(source: Path, output: Path, *, minimize: bool = False) -> dict[str, int | list[str]]: + """Package ``source`` (Rules directory) into ``output`` zip file.""" + source = source.resolve() + output = output.resolve() + + if not source.is_dir(): + raise FileNotFoundError(f"Rules source directory not found: {source}") + if source.name != "Rules": + raise ValueError(f"Expected a directory named 'Rules', got '{source.name}'") + + with tempfile.TemporaryDirectory(prefix="mathcat-rules-") as tmp: + staged_rules = Path(tmp) / "Rules" + if minimize: + minimized, copied, warnings = copy_rules_tree(source, staged_rules, minimize=True) + else: + shutil.copytree(source, staged_rules) + minimized, copied, warnings = 0, sum(1 for p in staged_rules.rglob("*") if p.is_file()), [] + + apply_release_layout(staged_rules) + zip_directory(staged_rules, output) + + return { + "minimized_yaml_files": minimized, + "copied_files": copied, + "warnings": warnings, + } def main(argv: list[str] | None = None) -> int: diff --git a/PythonScripts/rules_packaging/__init__.py b/PythonScripts/rules_packaging/__init__.py deleted file mode 100644 index 07c96c9d2..000000000 --- a/PythonScripts/rules_packaging/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Standalone Rules zip packaging and YAML minimization (not part of audit_translations).""" - -from .package import package_rules - -__all__ = ["package_rules"] diff --git a/PythonScripts/rules_packaging/minimize.py b/PythonScripts/rules_packaging/minimize.py deleted file mode 100644 index 7fb6a05d5..000000000 --- a/PythonScripts/rules_packaging/minimize.py +++ /dev/null @@ -1,108 +0,0 @@ -"""Convert MathCAT Rules YAML files to compact flow-style form.""" - -from __future__ import annotations - -import re -from pathlib import Path -from typing import Any - -import yaml - -YAML_SUFFIXES = {".yaml", ".yml"} -MINIMIZE_NAMES = {"unicode.yaml", "unicode-full.yaml"} - - -def should_minimize(path: Path) -> bool: - if path.suffix.lower() not in YAML_SUFFIXES or path.name not in MINIMIZE_NAMES: - return False - # Speech unicode only — braille unicode.yaml keeps block form (tests and edits rely on it). - return "Languages" in path.parts - - -def _configure_yaml_dumper() -> None: - """Quote all strings so yaml-rust does not treat words like 'infinity' as floats.""" - - def represent_str(dumper: yaml.SafeDumper, data: str) -> yaml.nodes.ScalarNode: - return dumper.represent_scalar("tag:yaml.org,2002:str", data, style='"') - - yaml.add_representer(str, represent_str, Dumper=yaml.SafeDumper) - - -_configure_yaml_dumper() - - -def preprocess_for_load(text: str) -> str: - """Normalize MathCAT YAML quirks so PyYAML can parse it.""" - text = text.replace("\t", " ") - # Bare double-quote or colon used as a mapping key in unicode list entries. - text = re.sub(r'(?m)^(\s*-\s*)":(\s*:\s*)', r'\1"\\"":\2', text) - text = re.sub(r'(?m)^(\s*-\s*)\\"(\s*:\s*)', r'\1"\\""\2', text) - return text - - -def load_yaml(text: str) -> Any: - """Load YAML using PyYAML with MathCAT-specific normalizations.""" - candidates = (text, text.replace("\t", " "), preprocess_for_load(text)) - last_error: yaml.YAMLError | None = None - for candidate in candidates: - try: - return yaml.safe_load(candidate) - except yaml.YAMLError as exc: - last_error = exc - if last_error is not None: - raise last_error - return None - - -def dump_flow_style(data: Any) -> str: - """Emit minimized flow-style YAML without comments.""" - if data is None: - return "" - dumped = yaml.dump( - data, - allow_unicode=True, - default_flow_style=True, - width=10**9, - sort_keys=False, - Dumper=yaml.SafeDumper, - ) - return dumped if dumped.endswith("\n") else f"{dumped}\n" - - -def minimize_yaml_text(text: str) -> tuple[str, bool]: - """Return flow-style YAML and whether parsing succeeded.""" - try: - data = load_yaml(text) - except yaml.YAMLError: - return text, False - return dump_flow_style(data), True - - -def copy_rules_tree(source: Path, destination: Path, *, minimize: bool) -> tuple[int, int, list[str]]: - """Copy ``source`` Rules tree to ``destination``, optionally minimizing YAML.""" - minimized = 0 - copied = 0 - warnings: list[str] = [] - - for src_path in sorted(source.rglob("*")): - if src_path.is_dir(): - continue - rel = src_path.relative_to(source) - dest_path = destination / rel - dest_path.parent.mkdir(parents=True, exist_ok=True) - - if minimize and should_minimize(src_path): - text = src_path.read_text(encoding="utf-8") - out, ok = minimize_yaml_text(text) - if ok: - dest_path.write_text(out, encoding="utf-8", newline="\n") - minimized += 1 - else: - dest_path.write_bytes(src_path.read_bytes()) - warnings.append(f"could not minimize {rel}; copied verbatim") - copied += 1 - else: - dest_path.write_bytes(src_path.read_bytes()) - copied += 1 - - return minimized, copied, warnings diff --git a/PythonScripts/rules_packaging/package.py b/PythonScripts/rules_packaging/package.py deleted file mode 100644 index 733662cb8..000000000 --- a/PythonScripts/rules_packaging/package.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Create Rules.zip archives from a Rules directory tree.""" - -from __future__ import annotations - -import shutil -import tempfile -from pathlib import Path - -from .compress import open_rules_zip -from .minimize import copy_rules_tree -from .release_layout import apply_release_layout - - -def zip_directory(source_dir: Path, zip_path: Path) -> None: - """Write ``source_dir`` contents into ``zip_path`` preserving relative paths.""" - zip_path.parent.mkdir(parents=True, exist_ok=True) - with open_rules_zip(zip_path) as archive: - for path in sorted(source_dir.rglob("*")): - if path.is_file(): - archive.write(path, path.relative_to(source_dir.parent).as_posix()) - - -def package_rules(source: Path, output: Path, *, minimize: bool = False) -> dict[str, int | list[str]]: - """Package ``source`` (Rules directory) into ``output`` zip file.""" - source = source.resolve() - output = output.resolve() - - if not source.is_dir(): - raise FileNotFoundError(f"Rules source directory not found: {source}") - if source.name != "Rules": - raise ValueError(f"Expected a directory named 'Rules', got '{source.name}'") - - with tempfile.TemporaryDirectory(prefix="mathcat-rules-") as tmp: - staging_root = Path(tmp) - staged_rules = staging_root / "Rules" - if minimize: - minimized, copied, warnings = copy_rules_tree(source, staged_rules, minimize=True) - else: - shutil.copytree(source, staged_rules) - minimized, copied, warnings = 0, sum(1 for p in staged_rules.rglob("*") if p.is_file()), [] - - apply_release_layout(staged_rules) - zip_directory(staged_rules, output) - - return { - "minimized_yaml_files": minimized, - "copied_files": copied, - "warnings": warnings, - } diff --git a/PythonScripts/rules_packaging/release_layout.py b/PythonScripts/rules_packaging/release_layout.py deleted file mode 100644 index 792687ad6..000000000 --- a/PythonScripts/rules_packaging/release_layout.py +++ /dev/null @@ -1,74 +0,0 @@ -"""Transform a Rules tree into the release layout used by build.rs and CI.""" - -from __future__ import annotations - -import shutil -from pathlib import Path - -from .compress import open_rules_zip - -YAML_SUFFIXES = {".yaml", ".yml"} -SKIP_LANGUAGE_DIRS = {"zz"} -ZIP_SECTIONS = ("Languages", "Braille") - - -def apply_release_layout(rules_dir: Path) -> None: - """Zip each language/braille subdir into ``/.zip`` and drop loose YAML inside.""" - languages = rules_dir / "Languages" - zz = languages / "zz" - if zz.is_dir(): - shutil.rmtree(zz) - - for section in ZIP_SECTIONS: - section_dir = rules_dir / section - if section_dir.is_dir(): - _zip_immediate_subdirs(section_dir) - - -def _zip_immediate_subdirs(parent: Path) -> None: - for entry in sorted(parent.iterdir(), key=lambda path: path.name): - if not entry.is_dir(): - continue - if entry.name in SKIP_LANGUAGE_DIRS: - shutil.rmtree(entry) - continue - - inner_zip = entry / f"{entry.name}.zip" - yaml_count = _write_yaml_zip(entry, inner_zip) - if yaml_count == 0: - inner_zip.unlink(missing_ok=True) - shutil.rmtree(entry) - continue - - _remove_files_except(entry, keep={inner_zip}) - _prune_empty_dirs(entry) - - -def _write_yaml_zip(source_dir: Path, zip_path: Path) -> int: - """Write all YAML under ``source_dir`` into ``zip_path``; return file count.""" - yaml_files = [ - path - for path in sorted(source_dir.rglob("*")) - if path.is_file() and path.suffix.lower() in YAML_SUFFIXES and path != zip_path - ] - if not yaml_files: - return 0 - - with open_rules_zip(zip_path) as archive: - for path in yaml_files: - archive.write(path, path.relative_to(source_dir).as_posix()) - return len(yaml_files) - - -def _remove_files_except(root: Path, *, keep: set[Path]) -> None: - for path in sorted(root.rglob("*"), reverse=True): - if path.is_file() and path.resolve() not in {item.resolve() for item in keep}: - path.unlink() - - -def _prune_empty_dirs(root: Path) -> None: - for path in sorted((item for item in root.rglob("*") if item.is_dir()), reverse=True): - try: - path.rmdir() - except OSError: - continue diff --git a/PythonScripts/rules_packaging/tests/__init__.py b/PythonScripts/rules_packaging/tests/__init__.py deleted file mode 100644 index b5d3c1aa2..000000000 --- a/PythonScripts/rules_packaging/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Tests for rules packaging.""" diff --git a/PythonScripts/rules_packaging/tests/test_package.py b/PythonScripts/tests/test_package_rules.py similarity index 95% rename from PythonScripts/rules_packaging/tests/test_package.py rename to PythonScripts/tests/test_package_rules.py index 334c5b851..216a1f05d 100644 --- a/PythonScripts/rules_packaging/tests/test_package.py +++ b/PythonScripts/tests/test_package_rules.py @@ -8,9 +8,13 @@ import yaml -from rules_packaging.compress import RULES_ZIP_COMPRESSION -from rules_packaging.minimize import dump_flow_style, load_yaml, minimize_yaml_text -from rules_packaging.package import package_rules +from package_rules import ( + RULES_ZIP_COMPRESSION, + dump_flow_style, + load_yaml, + minimize_yaml_text, + package_rules, +) def read_nested_zip_text(archive: zipfile.ZipFile, outer_path: str, inner_path: str) -> str: From 3bb491df6542e4f4af8bacb58fb7b73ef973ac54 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 17 Aug 2026 17:12:14 -0700 Subject: [PATCH 051/120] AI keeps messing with things it shouldn't... --- .github/workflows/python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index c2bb772bf..548e25a92 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -17,4 +17,4 @@ jobs: run: uv run ruff format --check audit_translations/ - name: Run tests working-directory: PythonScripts - run: uv run pytest + run: uv run pytest tests/test_package_rules.py From 7af9ec3d2bb7704db9da175c14639f962d2cce13 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Mon, 17 Aug 2026 17:19:20 -0700 Subject: [PATCH 052/120] remove inappropriate check --- PythonScripts/tests/test_package_rules.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/PythonScripts/tests/test_package_rules.py b/PythonScripts/tests/test_package_rules.py index 216a1f05d..ced3a82e5 100644 --- a/PythonScripts/tests/test_package_rules.py +++ b/PythonScripts/tests/test_package_rules.py @@ -104,13 +104,10 @@ def test_package_rules_minimized_matches_release_layout(tmp_path: Path) -> None: encoding="utf-8", ) - standard = tmp_path / "Rules.zip" minimized = tmp_path / "Rules-minimized.zip" - package_rules(rules, standard, minimize=False) stats = package_rules(rules, minimized, minimize=True) assert stats["minimized_yaml_files"] == 1 - assert minimized.stat().st_size <= standard.stat().st_size with zipfile.ZipFile(minimized) as archive: names = archive.namelist() From 786d058ece11aa14a1f00004cc31f446dfa617f3 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Tue, 18 Aug 2026 00:10:55 -0700 Subject: [PATCH 053/120] Unify Rules packaging in Rust for CI, include-zip, and release zips. Replace the Python packager with shared rules_archive logic and a package-rules binary so build.rs, downloadable archives, and the include-zip embed use one layout (inner BZIP2; outer DEFLATE for unzip-friendly downloads). Serialize parallel language unzip during tests to avoid corrupting the shared Rules tree. Co-authored-by: Cursor --- .github/workflows/python.yml | 2 +- .github/workflows/rust.yml | 10 +- AGENTS.md | 4 +- Cargo.toml | 15 +- PythonScripts/package_rules.py | 265 --------------- PythonScripts/tests/test_package_rules.py | 120 ------- build.rs | 206 +----------- src/bin/package_rules.rs | 230 +++++++++++++ src/interface.rs | 3 +- src/prefs.rs | 19 +- src/rules_archive.rs | 385 ++++++++++++++++++++++ 11 files changed, 659 insertions(+), 600 deletions(-) delete mode 100644 PythonScripts/package_rules.py delete mode 100644 PythonScripts/tests/test_package_rules.py create mode 100644 src/bin/package_rules.rs create mode 100644 src/rules_archive.rs diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 548e25a92..c2bb772bf 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -17,4 +17,4 @@ jobs: run: uv run ruff format --check audit_translations/ - name: Run tests working-directory: PythonScripts - run: uv run pytest tests/test_package_rules.py + run: uv run pytest diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 8b60e93a5..e36f9b4b2 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -46,15 +46,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: astral-sh/setup-uv@v10.0.1 - - name: Remove empty Rules directories - run: find Rules -type d -empty -delete - - name: Remove test directory - run: rm -r Rules/Languages/zz + - uses: dtolnay/rust-toolchain@stable - name: Create Rules.zip - run: uv run --no-project --script PythonScripts/package_rules.py Rules Rules.zip + run: cargo run --bin package-rules -- Rules Rules.zip - name: Create Rules-minimized.zip - run: uv run --no-project --script PythonScripts/package_rules.py Rules Rules-minimized.zip --minimize + run: cargo run --bin package-rules -- Rules Rules-minimized.zip --minimize - name: Upload Rules.zip uses: actions/upload-artifact@v7 with: diff --git a/AGENTS.md b/AGENTS.md index e4076ece7..76ea6a889 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,8 +12,8 @@ but add common mistakes of AI agents here instead. - Common per-language files: - `ClearSpeak_Rules.yaml`, `SimpleSpeak_Rules.yaml` - `SharedRules/`, `unicode.yaml`, `unicode-full.yaml`, `definitions.yaml`, `navigate.yaml` -- `build.rs` can bundle rules into `rules.zip` when `include-zip` is enabled. -- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style `Languages/**/unicode*.yaml` only) via `uv run --no-project --script PythonScripts/package_rules.py Rules [--minimize]`. Each language/braille subdir is stored as `/.zip` (matching `build.rs`); top-level and `Intent/` YAML stay loose. Both omit `Languages/zz`; the `test-rules-package` job restores `zz` from git after unzip so unit tests still run against release-like archives. +- `build.rs` can bundle rules into `rules.zip` when `include-zip` is enabled (always the minimized tree). Shared zipper: `src/rules_archive.rs`, invoked as `cargo run --bin package-rules -- Rules [--minimize]`. +- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (comments stripped from `Languages/**/unicode.yaml` and `unicode-full.yaml` only). Each language/braille subdir is stored as `/.zip`; top-level and `Intent/` YAML stay loose. Inner language zips use BZIP2; the outer downloadable archive uses DEFLATE so `unzip` works. Both omit `Languages/zz`; the `test-rules-package` job restores `zz` from git after unzip so unit tests still run against release-like archives. ## Translation Conventions - `t:` means untranslated or unverified. diff --git a/Cargo.toml b/Cargo.toml index 45de0ad1f..32e83cf27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,18 +39,17 @@ fastrand = { version = "2.3.0" } clap = { version = "4.5.60", features = ["derive"] } html-escape = "0.2.13" -[target.'cfg(target_family = "wasm")'.dependencies] -zip = { version = "8.2", default-features = false, features = ["deflate"] } -[target.'cfg(not(target_family = "wasm"))'.dependencies] -zip = { version = "8.2", default-features = false, features = ["bzip2"] } +# Runtime extracts inner language/braille zips (BZIP2). The package-rules binary also +# writes DEFLATE outer archives for Windows/unzip-compatible Rules.zip downloads. +# wasm uses the same features: zip 8.x bzip2 is pure Rust (libbz2-rs-sys), not clang C. +zip = { version = "8.2", default-features = false, features = ["deflate", "bzip2"] } android_logger = {version = "0.15.1", optional = true} natural-tts = { version = "0.3.1", optional = true } [build-dependencies] bitflags = "2.6" -# Build script needs both compression backends regardless of build host — -# build.rs picks DEFLATE/BZIP2 based on the *target* family, not the host. +yaml-rust = "0.4" zip = { version = "8.2", default-features = false, features = ["deflate", "bzip2"] } [dev-dependencies] @@ -64,6 +63,10 @@ crate-type = ["rlib", "cdylib"] name = "mathml2text" path = "src/bin/mathml2text.rs" +[[bin]] +name = "package-rules" +path = "src/bin/package_rules.rs" + [lints.rust] unexpected_cfgs = { level = "allow", check-cfg = ['cfg(coverage)', 'cfg(coverage_nightly)'] } diff --git a/PythonScripts/package_rules.py b/PythonScripts/package_rules.py deleted file mode 100644 index 53ff2d89e..000000000 --- a/PythonScripts/package_rules.py +++ /dev/null @@ -1,265 +0,0 @@ -# /// script -# requires-python = ">=3.14" -# dependencies = ["pyyaml"] -# /// -"""Package MathCAT Rules/ into Rules.zip or Rules-minimized.zip.""" - -from __future__ import annotations - -import argparse -import re -import shutil -import sys -import tempfile -import zipfile -from pathlib import Path -from typing import Any - -import yaml - -YAML_SUFFIXES = {".yaml", ".yml"} -MINIMIZE_NAMES = {"unicode.yaml", "unicode-full.yaml"} -SKIP_LANGUAGE_DIRS = {"zz"} -ZIP_SECTIONS = ("Languages", "Braille") - -# Non-WASM MathCAT links the `zip` crate with only the `bzip2` feature (see Cargo.toml). -# Inner language/braille zips must use BZIP2 — DEFLATE (method 8) fails at runtime with -# CompressionMethodNotSupported. build.rs uses the same choice for native targets. -RULES_ZIP_COMPRESSION = zipfile.ZIP_BZIP2 -RULES_ZIP_COMPRESSLEVEL = 9 - - -def _open_rules_zip(path: Path, mode: str = "w") -> zipfile.ZipFile: - return zipfile.ZipFile( - path, - mode, - compression=RULES_ZIP_COMPRESSION, - compresslevel=RULES_ZIP_COMPRESSLEVEL, - ) - - -def _configure_yaml_dumper() -> None: - """Quote all strings so yaml-rust does not treat words like 'infinity' as floats.""" - - def represent_str(dumper: yaml.SafeDumper, data: str) -> yaml.nodes.ScalarNode: - return dumper.represent_scalar("tag:yaml.org,2002:str", data, style='"') - - yaml.add_representer(str, represent_str, Dumper=yaml.SafeDumper) - - -_configure_yaml_dumper() - - -def should_minimize(path: Path) -> bool: - if path.suffix.lower() not in YAML_SUFFIXES or path.name not in MINIMIZE_NAMES: - return False - # Speech unicode only — braille unicode.yaml keeps block form (tests and edits rely on it). - return "Languages" in path.parts - - -def preprocess_for_load(text: str) -> str: - """Normalize MathCAT YAML quirks so PyYAML can parse it.""" - text = text.replace("\t", " ") - text = re.sub(r'(?m)^(\s*-\s*)":(\s*:\s*)', r'\1"\\"":\2', text) - text = re.sub(r'(?m)^(\s*-\s*)\\"(\s*:\s*)', r'\1"\\""\2', text) - return text - - -def load_yaml(text: str) -> Any: - """Load YAML using PyYAML with MathCAT-specific normalizations.""" - candidates = (text, text.replace("\t", " "), preprocess_for_load(text)) - last_error: yaml.YAMLError | None = None - for candidate in candidates: - try: - return yaml.safe_load(candidate) - except yaml.YAMLError as exc: - last_error = exc - if last_error is not None: - raise last_error - return None - - -def dump_flow_style(data: Any) -> str: - """Emit minimized flow-style YAML without comments.""" - if data is None: - return "" - dumped = yaml.dump( - data, - allow_unicode=True, - default_flow_style=True, - width=10**9, - sort_keys=False, - Dumper=yaml.SafeDumper, - ) - return dumped if dumped.endswith("\n") else f"{dumped}\n" - - -def minimize_yaml_text(text: str) -> tuple[str, bool]: - """Return flow-style YAML and whether parsing succeeded.""" - try: - data = load_yaml(text) - except yaml.YAMLError: - return text, False - return dump_flow_style(data), True - - -def copy_rules_tree(source: Path, destination: Path, *, minimize: bool) -> tuple[int, int, list[str]]: - """Copy ``source`` Rules tree to ``destination``, optionally minimizing YAML.""" - minimized = 0 - copied = 0 - warnings: list[str] = [] - - for src_path in sorted(source.rglob("*")): - if src_path.is_dir(): - continue - rel = src_path.relative_to(source) - dest_path = destination / rel - dest_path.parent.mkdir(parents=True, exist_ok=True) - - if minimize and should_minimize(src_path): - text = src_path.read_text(encoding="utf-8") - out, ok = minimize_yaml_text(text) - if ok: - dest_path.write_text(out, encoding="utf-8", newline="\n") - minimized += 1 - else: - dest_path.write_bytes(src_path.read_bytes()) - warnings.append(f"could not minimize {rel}; copied verbatim") - copied += 1 - else: - dest_path.write_bytes(src_path.read_bytes()) - copied += 1 - - return minimized, copied, warnings - - -def apply_release_layout(rules_dir: Path) -> None: - """Zip each language/braille subdir into ``/.zip`` and drop loose YAML inside.""" - zz = rules_dir / "Languages" / "zz" - if zz.is_dir(): - shutil.rmtree(zz) - - for section in ZIP_SECTIONS: - section_dir = rules_dir / section - if section_dir.is_dir(): - _zip_immediate_subdirs(section_dir) - - -def _zip_immediate_subdirs(parent: Path) -> None: - for entry in sorted(parent.iterdir(), key=lambda path: path.name): - if not entry.is_dir(): - continue - if entry.name in SKIP_LANGUAGE_DIRS: - shutil.rmtree(entry) - continue - - inner_zip = entry / f"{entry.name}.zip" - yaml_count = _write_yaml_zip(entry, inner_zip) - if yaml_count == 0: - inner_zip.unlink(missing_ok=True) - shutil.rmtree(entry) - continue - - _remove_files_except(entry, keep={inner_zip}) - _prune_empty_dirs(entry) - - -def _write_yaml_zip(source_dir: Path, zip_path: Path) -> int: - yaml_files = [ - path - for path in sorted(source_dir.rglob("*")) - if path.is_file() and path.suffix.lower() in YAML_SUFFIXES and path != zip_path - ] - if not yaml_files: - return 0 - - with _open_rules_zip(zip_path) as archive: - for path in yaml_files: - archive.write(path, path.relative_to(source_dir).as_posix()) - return len(yaml_files) - - -def _remove_files_except(root: Path, *, keep: set[Path]) -> None: - keep_resolved = {item.resolve() for item in keep} - for path in sorted(root.rglob("*"), reverse=True): - if path.is_file() and path.resolve() not in keep_resolved: - path.unlink() - - -def _prune_empty_dirs(root: Path) -> None: - for path in sorted((item for item in root.rglob("*") if item.is_dir()), reverse=True): - try: - path.rmdir() - except OSError: - continue - - -def zip_directory(source_dir: Path, zip_path: Path) -> None: - """Write ``source_dir`` contents into ``zip_path`` preserving relative paths.""" - zip_path.parent.mkdir(parents=True, exist_ok=True) - with _open_rules_zip(zip_path) as archive: - for path in sorted(source_dir.rglob("*")): - if path.is_file(): - archive.write(path, path.relative_to(source_dir.parent).as_posix()) - - -def package_rules(source: Path, output: Path, *, minimize: bool = False) -> dict[str, int | list[str]]: - """Package ``source`` (Rules directory) into ``output`` zip file.""" - source = source.resolve() - output = output.resolve() - - if not source.is_dir(): - raise FileNotFoundError(f"Rules source directory not found: {source}") - if source.name != "Rules": - raise ValueError(f"Expected a directory named 'Rules', got '{source.name}'") - - with tempfile.TemporaryDirectory(prefix="mathcat-rules-") as tmp: - staged_rules = Path(tmp) / "Rules" - if minimize: - minimized, copied, warnings = copy_rules_tree(source, staged_rules, minimize=True) - else: - shutil.copytree(source, staged_rules) - minimized, copied, warnings = 0, sum(1 for p in staged_rules.rglob("*") if p.is_file()), [] - - apply_release_layout(staged_rules) - zip_directory(staged_rules, output) - - return { - "minimized_yaml_files": minimized, - "copied_files": copied, - "warnings": warnings, - } - - -def main(argv: list[str] | None = None) -> int: - parser = argparse.ArgumentParser( - description="Package MathCAT Rules/ into a zip archive. " - "Use --minimize for flow-style unicode.yaml / unicode-full.yaml without comments." - ) - parser.add_argument("source", type=Path, help="Path to the Rules directory") - parser.add_argument("output", type=Path, help="Output zip file path (e.g. Rules.zip)") - parser.add_argument( - "--minimize", - action="store_true", - help="Emit compact flow-style YAML (comments and block formatting removed)", - ) - args = parser.parse_args(argv) - - try: - stats = package_rules(args.source, args.output, minimize=args.minimize) - except (FileNotFoundError, ValueError) as exc: - print(f"error: {exc}", file=sys.stderr) - return 1 - - label = "minimized" if args.minimize else "standard" - print(f"Created {label} archive: {args.output}") - if args.minimize: - print(f" minimized YAML files: {stats['minimized_yaml_files']}") - print(f" copied verbatim: {stats['copied_files']}") - for warning in stats["warnings"]: - print(f" warning: {warning}", file=sys.stderr) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/PythonScripts/tests/test_package_rules.py b/PythonScripts/tests/test_package_rules.py deleted file mode 100644 index ced3a82e5..000000000 --- a/PythonScripts/tests/test_package_rules.py +++ /dev/null @@ -1,120 +0,0 @@ -"""Tests for Rules packaging and YAML minimization.""" - -from __future__ import annotations - -import io -import zipfile -from pathlib import Path - -import yaml - -from package_rules import ( - RULES_ZIP_COMPRESSION, - dump_flow_style, - load_yaml, - minimize_yaml_text, - package_rules, -) - - -def read_nested_zip_text(archive: zipfile.ZipFile, outer_path: str, inner_path: str) -> str: - """Read ``inner_path`` from a zip file stored inside the outer archive.""" - with zipfile.ZipFile(io.BytesIO(archive.read(outer_path))) as inner: - return inner.read(inner_path).decode("utf-8") - - -def test_minimize_yaml_strips_comments_and_uses_flow_style() -> None: - """Minimized YAML drops comments and uses flow-style collections.""" - source = """--- -# comment - - \"+\": [t: \"plus\"] # inline -""" - minimized, ok = minimize_yaml_text(source) - assert ok - assert "#" not in minimized - assert "[" in minimized and "{" in minimized or "t:" in minimized - assert load_yaml(minimized) == load_yaml(source) - - -def test_package_rules_standard_zip_layout(tmp_path: Path) -> None: - """Standard packaging preserves Rules/ prefix and per-language inner zips.""" - rules = tmp_path / "Rules" - lang = rules / "Languages" / "en" - lang.mkdir(parents=True) - (rules / "prefs.yaml").write_text("- SpeechStyle: [t: \"ClearSpeak\"]\n", encoding="utf-8") - (lang / "unicode.yaml").write_text("- \"+\": [t: \"plus\"]\n", encoding="utf-8") - - output = tmp_path / "Rules.zip" - package_rules(rules, output, minimize=False) - - with zipfile.ZipFile(output) as archive: - names = archive.namelist() - assert "Rules/prefs.yaml" in names - assert "Rules/Languages/en/en.zip" in names - assert "Rules/Languages/en/unicode.yaml" not in names - with zipfile.ZipFile(io.BytesIO(archive.read("Rules/Languages/en/en.zip"))) as inner: - assert all(info.compress_type == RULES_ZIP_COMPRESSION for info in inner.infolist()) - assert read_nested_zip_text(archive, "Rules/Languages/en/en.zip", "unicode.yaml").replace("\r\n", "\n") == "- \"+\": [t: \"plus\"]\n" - - -def test_dump_flow_style_quotes_infinity() -> None: - """Flow-style dump must quote 'infinity' so yaml-rust does not read it as a float.""" - minimized = dump_flow_style([{"∞": [{"t": "infinity"}]}]) - assert '"infinity"' in minimized - assert "t: infinity" not in minimized - - -def test_should_not_minimize_braille_unicode(tmp_path: Path) -> None: - """Braille unicode.yaml is copied verbatim even when minimization is enabled.""" - rules = tmp_path / "Rules" - braille = rules / "Braille" / "Nemeth" - braille.mkdir(parents=True) - source = """--- - - "1": [t: "N⠂"] -""" - (braille / "unicode.yaml").write_text(source, encoding="utf-8") - - output = tmp_path / "Rules-minimized.zip" - stats = package_rules(rules, output, minimize=True) - - assert stats["minimized_yaml_files"] == 0 - with zipfile.ZipFile(output) as archive: - text = read_nested_zip_text(archive, "Rules/Braille/Nemeth/Nemeth.zip", "unicode.yaml") - assert text.replace("\r\n", "\n") == source.replace("\r\n", "\n") - - -def test_package_rules_minimized_matches_release_layout(tmp_path: Path) -> None: - """Minimized archives use inner language zips and flow-style unicode YAML.""" - rules = tmp_path / "Rules" - lang = rules / "Languages" / "en" - lang.mkdir(parents=True) - (lang / "unicode.yaml").write_text( - """--- -# header comment - - \"+\": [t: \"plus\"] # trailing - - \"-\": [t: \"minus\"] -""", - encoding="utf-8", - ) - (lang / "definitions.yaml").write_text( - """--- - - AdditionalFunctionNames: - - real=inf -""", - encoding="utf-8", - ) - - minimized = tmp_path / "Rules-minimized.zip" - stats = package_rules(rules, minimized, minimize=True) - - assert stats["minimized_yaml_files"] == 1 - - with zipfile.ZipFile(minimized) as archive: - names = archive.namelist() - assert "Rules/Languages/en/en.zip" in names - assert "Rules/Languages/en/unicode.yaml" not in names - unicode_text = read_nested_zip_text(archive, "Rules/Languages/en/en.zip", "unicode.yaml") - defs_text = read_nested_zip_text(archive, "Rules/Languages/en/en.zip", "definitions.yaml") - assert "#" not in unicode_text - assert "real=inf" in defs_text - assert yaml.safe_load(unicode_text) is not None diff --git a/build.rs b/build.rs index a48b26033..e5a07cd8d 100644 --- a/build.rs +++ b/build.rs @@ -1,205 +1,31 @@ //! The build.rs file is necessary to generate rules.zip. -//! rules.zip are needed so there is a way to get the rules dir into the build since your can't get from the crate. +//! rules.zip are needed so there is a way to get the rules dir into the build since you can't get from the crate. //! The expectation is that most builds (with the exception of WASM builds) will need a build.rs file to extract the rules. #![allow(clippy::needless_return)] +#![allow(dead_code)] // include! of rules_archive.rs pulls in the downloadable packager too -use std::fs::{read_dir, DirBuilder, File, remove_file, remove_dir}; -use std::io::{self, Read, Seek, Write}; -use std::path::{Path, PathBuf}; +include!("src/rules_archive.rs"); - -// use zip::result::ZipResult; -use zip::write::{ZipWriter, SimpleFileOptions}; -use zip::CompressionMethod; - -/// Zip up all the yaml files in the directories in `rules_dir/sub_dir_name`` and add the zipped file to `archive_zip`. -/// `rules_dir` is the full path to MathCAT's Rules dir (the input), with `sub_dir_name` the (relative) name of the sub dir to look into. -/// `out_dir`` is the full to the build dir's "out" file (where we are allowed to write) -/// For example, if we have the input ".../Rules/Langauges", then we create a zip file for each subdir with yaml files in it. -/// So we get out_dir/Rules/Langauges/en/en.zip, out_dir/Rules/Langauges/es/es.zip, etc. -/// The resulting archive will reproduce the Rules tree but with the yaml files zipped up per/directory. -fn zip_dir(rules_dir: &Path, archive_zip: &mut ZipWriter, options: SimpleFileOptions, out_dir: &Path, current_out_dir: &Path) -> Result<(), std::io::Error> { - for entry in read_dir(rules_dir)?.flatten() { - let entry_path = entry.path(); - - // .zip files return true for is_dir() -- test in case there is some leftover zip files - if let Some(suffix) = entry_path.extension() && suffix == "zip" { - continue; - } - // println!("trying dir entry {:?}", entry_path.to_str()); - if entry_path.is_dir(){ - if let Some(dir_name) = entry_path.components().next_back() { - let dir_name = dir_name.as_os_str().to_str().unwrap(); - if dir_name == "zz" { // test dir - continue; - } - let zip_name = dir_name.to_string() + ".zip"; // e.g., en.zip - // println!("cargo::warning=zip_name='{}'", &zip_name); - let current_out_dir_zip_path = current_out_dir.join(dir_name); // e.g., ...out/Rules/Languages/en - // println!("cargo::warning=current_out_dir_zip_path='{}'", ¤t_out_dir_zip_path.to_string_lossy()); - - // make sure the appropriate directory exists in 'out' - DirBuilder::new().recursive(true).create(¤t_out_dir_zip_path).unwrap(); - let zipped_dir_file_name = current_out_dir_zip_path.join(&zip_name); // e.g., ...out/Rules/Languages/en/en.zip - let zip_file = match File::create(&zipped_dir_file_name) { - Ok(file) => file, - Err(e) => return Err(std::io::Error::other( - format!("build.rs couldn't create {:?}: {}", zip_name, e))), - }; - let mut zip = ZipWriter::new(zip_file); - let n_files_in_zip = zip_entry(&mut zip, &entry_path, &PathBuf::from("."), options)?; - zip.finish()?; - if n_files_in_zip > 0 { - // Add the file to full archive with the proper relative path (e.g., Languages/en) - let relative_out_dir = current_out_dir_zip_path.strip_prefix(out_dir).unwrap(); // e.g., Rules/Langauges/en - add_file_to_zip(archive_zip, &zipped_dir_file_name, &relative_out_dir.join(&zip_name), options)?; - } else { - // delete the .zip file that isn't used -- doesn't really matter, but removes some clutter - remove_file(&zipped_dir_file_name)?; - remove_dir(¤t_out_dir_zip_path)?; - } - } - } else if let Some(suffix) = &entry_path.extension() { - // definitions.yaml, others??? - let suffix = suffix.to_ascii_lowercase(); - if suffix == "yaml" || suffix == "yml" { - // make sure the appropriate directory exists in 'out' - DirBuilder::new().recursive(true).create(current_out_dir).unwrap(); - let entry_name = entry_path.components().next_back().unwrap().as_os_str().to_str().unwrap(); - let out_dir_file_name = current_out_dir.join(entry_name); // e.g., ...out/Rules/prefs.yaml - std::fs::copy(&entry_path, &out_dir_file_name)?; - let relative_out_dir = out_dir_file_name.strip_prefix(out_dir).unwrap(); - add_file_to_zip(archive_zip, relative_out_dir, relative_out_dir, options)?; - - } - } - } - return Ok( () ); -} - -/// This differs from `zip_dir` in that it does _not_ zip up the directory. Each individual file is zipped so that when the full archive -/// is unzipped, the directory contents do not need to be unzipped. -/// For example, Rules/prefs.yaml and Rules/Intent/general.yaml will exists in the unzipped archive. -fn zip_other_files(rules_dir: &Path, archive_zip: &mut ZipWriter, options: SimpleFileOptions, out_dir: &Path, current_out_dir: &Path) -> Result<(), std::io::Error> { - for entry in read_dir(rules_dir)? { - let entry = entry?; - let entry_path = entry.path(); - let entry_name = entry_path.components().next_back().unwrap().as_os_str().to_str().unwrap(); - - if entry_path.is_dir(){ - if entry_name == "Intent" { // handled elsewhere - zip_other_files(&rules_dir.join("Intent"), archive_zip, options, out_dir, ¤t_out_dir.join("Intent"))?; - } - } else if let Some(suffix) = &entry_path.extension() { - let suffix = suffix.to_ascii_lowercase(); - if suffix == "yaml" || suffix == "yml" { - // make sure the appropriate directory exists in 'out' - DirBuilder::new().recursive(true).create(current_out_dir).unwrap(); - let out_dir_file_name = current_out_dir.join(entry_name); // e.g., ...out/Rules/prefs.yaml - std::fs::copy(&entry_path, &out_dir_file_name)?; - let relative_out_dir = out_dir_file_name.strip_prefix(out_dir).unwrap(); - add_file_to_zip(archive_zip, relative_out_dir, relative_out_dir, options)?; - - } - } - } - return Ok( () ); -} - -/// Adds the `path` relative to the outdir to the `zip`` archive -fn add_file_to_zip( - zip: &mut ZipWriter, - in_dir_path: &Path, - out_dir_path: &Path, - options: SimpleFileOptions, -) -> io::Result<()> { - zip.start_file_from_path(out_dir_path, options)?; - let mut file = File::open(in_dir_path)?; - let mut buffer = Vec::new(); - - file.read_to_end(&mut buffer)?; - zip.write_all(&buffer)?; - return Ok(()); -} - -#[allow(clippy::unused_io_amount)] -fn zip_entry( - zip: &mut ZipWriter, - full_path: &Path, - relative_path: &Path, - options: SimpleFileOptions -) -> io::Result { - let mut n_files_in_zip = 0; - // println!("zip_entry:\n full_path: {}\n relative_path: {}\n current working dir: {}", full_path.display(), relative_path.display(), std::env::current_dir()?.display()); - if full_path.is_dir() { - for entry in read_dir(full_path)? { - let entry_path = entry?.path(); - let entry_name = entry_path.components().next_back().unwrap().as_os_str().to_str().unwrap(); - n_files_in_zip += zip_entry(zip, &entry_path, &relative_path.join(entry_name), options)?; - } - } else if let Some(suffix) = full_path.extension() { - let suffix = suffix.to_ascii_lowercase(); - if suffix == "yaml" || suffix == "yml" { - add_file_to_zip(zip, full_path, relative_path, options)?; - n_files_in_zip += 1; - } - } - - return Ok(n_files_in_zip); -} - -/// Zip up each language and braille dir -/// Note: regional variations (including zh-cn and zh-tw) are zipped together into one zip file fn main() { println!("cargo::rerun-if-changed=build.rs"); + println!("cargo::rerun-if-changed=src/rules_archive.rs"); println!("cargo::rerun-if-changed=Rules"); // This doesn't work because the build claims OUT_DIR is not defined(?) // let archive = PathBuf::from(concat!(env!("OUT_DIR"),"/rules.zip")); if std::env::var("CARGO_FEATURE_INCLUDE_ZIP").is_ok() { - let out_dir = std::env::var_os("OUT_DIR").unwrap(); - let out_dir = PathBuf::from(&out_dir); - let rules_dir = std::env::current_dir().unwrap().join("Rules") ; - let rules_out_dir = PathBuf::from(&out_dir).join("Rules"); - - if std::env::set_current_dir(&out_dir).is_err() { - println!("cargo::warning=couldn't change to directory '{}'", out_dir.display()); - return; - } - let archive_path = PathBuf::from("rules.zip"); // A zip file containing all the zip files. - // println!("cargo::warning=zip file location: '{:?}'", archive_path.to_str()); - let compile_target = std::env::var("CARGO_CFG_TARGET_FAMILY").unwrap(); // build.rs has target_family = build machine, not real target - let compression_method = if compile_target == "wasm" { - CompressionMethod::DEFLATE // although BZIP2 seems better in terms of smaller, it won't compile for WASM - } else { - CompressionMethod::BZIP2 - }; - let zip_options = SimpleFileOptions::default().compression_method(compression_method) - .compression_level(Some(9)); - // let zip_options = SimpleFileOptions::default() - // .compression_level(Some(9)); - - // println!("cargo::warning=rules directory '{:?}'", &rules_dir.to_string_lossy()); - let archive_zip_file = match File::create(&archive_path) { - Ok(file) => file, - Err(e) => panic!("build.rs couldn't create {:?}: {}", archive_path.to_str(), e), - }; - - let mut archive_zip = ZipWriter::new(archive_zip_file); - - - if let Err(e) = zip_other_files(&rules_dir, &mut archive_zip, zip_options, &out_dir, &rules_out_dir) { - panic!("Error: {}", e); - } - - if let Err(e) = zip_dir(&rules_dir.join("Languages"), &mut archive_zip, zip_options, &out_dir, &rules_out_dir.join("Languages")) { - panic!("Error: {}", e); - } - if let Err(e) = zip_dir(&rules_dir.join("Braille"), &mut archive_zip, zip_options, &out_dir, &rules_out_dir.join("Braille")) { - panic!("Error: {}", e); - } - if let Err(e) = archive_zip.finish() { - panic!("Error in zip.finish(): {}", e); + let out_dir = PathBuf::from(std::env::var_os("OUT_DIR").unwrap()); + let rules_dir = std::env::current_dir().unwrap().join("Rules"); + let staging = out_dir.join("rules_src"); + if staging.exists() { + let _ = std::fs::remove_dir_all(&staging); + } + if let Err(e) = copy_rules_tree(&rules_dir, &staging, true) { + panic!("build.rs failed to stage minimized Rules: {}", e); + } + let archive_path = out_dir.join("rules.zip"); + if let Err(e) = write_rules_archive(&staging, &archive_path, include_zip_compression()) { + panic!("build.rs failed to write {}: {}", archive_path.display(), e); } } } diff --git a/src/bin/package_rules.rs b/src/bin/package_rules.rs new file mode 100644 index 000000000..261059bea --- /dev/null +++ b/src/bin/package_rules.rs @@ -0,0 +1,230 @@ +//! Package `Rules/` into `Rules.zip` or `Rules-minimized.zip`. +//! +//! Usage: +//! cargo run --bin package-rules -- Rules Rules.zip +//! cargo run --bin package-rules -- Rules Rules-minimized.zip --minimize + +#![allow(clippy::needless_return)] + +include!("../rules_archive.rs"); + +use clap::Parser; +use std::process::ExitCode; + +#[derive(Parser)] +#[command( + name = "package-rules", + about = "Package MathCAT Rules/ into a zip archive. \ + Use --minimize to strip comments from Languages unicode YAML." +)] +struct Options { + /// Path to the Rules directory + source: PathBuf, + /// Output zip file path (e.g. Rules.zip) + output: PathBuf, + /// Emit compact unicode.yaml / unicode-full.yaml without comments (speech languages only) + #[arg(long)] + minimize: bool, +} + +fn main() -> ExitCode { + let cli = Options::parse(); + if cli.source.file_name().and_then(|n| n.to_str()) != Some("Rules") { + eprintln!( + "error: expected a directory named 'Rules', got '{}'", + cli.source.display() + ); + return ExitCode::from(1); + } + + match package_rules( + &cli.source, + &cli.output, + cli.minimize, + downloadable_compression(), + ) { + Ok(minimized) => { + let label = if cli.minimize { "minimized" } else { "standard" }; + println!("Created {} archive: {}", label, cli.output.display()); + if cli.minimize { + println!(" minimized YAML files: {}", minimized); + } + return ExitCode::SUCCESS; + } + Err(e) => { + eprintln!("error: {}", e); + return ExitCode::from(1); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + use std::io::{Cursor, Read}; + use tempfile::tempdir; + use zip::ZipArchive; + + fn read_nested_zip_text(outer: &[u8], outer_path: &str, inner_path: &str) -> String { + let mut archive = ZipArchive::new(Cursor::new(outer)).unwrap(); + let mut inner_bytes = Vec::new(); + archive + .by_name(outer_path) + .unwrap() + .read_to_end(&mut inner_bytes) + .unwrap(); + let mut inner = ZipArchive::new(Cursor::new(inner_bytes)).unwrap(); + let mut text = String::new(); + inner + .by_name(inner_path) + .unwrap() + .read_to_string(&mut text) + .unwrap(); + return text; + } + + fn outer_names(bytes: &[u8]) -> Vec { + let mut archive = ZipArchive::new(Cursor::new(bytes)).unwrap(); + let mut names: Vec = (0..archive.len()) + .map(|i| archive.by_index(i).unwrap().name().replace('\\', "/")) + .collect(); + names.sort(); + return names; + } + + fn inner_method(bytes: &[u8], outer_path: &str) -> zip::CompressionMethod { + let mut archive = ZipArchive::new(Cursor::new(bytes)).unwrap(); + let mut inner_bytes = Vec::new(); + archive + .by_name(outer_path) + .unwrap() + .read_to_end(&mut inner_bytes) + .unwrap(); + let mut inner = ZipArchive::new(Cursor::new(inner_bytes)).unwrap(); + return inner.by_index(0).unwrap().compression(); + } + + fn outer_method(bytes: &[u8], name: &str) -> zip::CompressionMethod { + let mut archive = ZipArchive::new(Cursor::new(bytes)).unwrap(); + return archive.by_name(name).unwrap().compression(); + } + + #[test] + fn minimize_yaml_strips_comments() { + let source = "---\n# comment\n - \"+\": [t: \"plus\"] # inline\n"; + let minimized = minimize_yaml_text(source).unwrap(); + assert!(!minimized.contains('#')); + let docs = yaml_rust::YamlLoader::load_from_str(&minimized).unwrap(); + assert_eq!(docs.len(), 1); + } + + #[test] + fn minimize_quotes_infinity() { + let source = "---\n - \"∞\": [t: \"infinity\"]\n"; + let minimized = minimize_yaml_text(source).unwrap(); + assert!( + minimized.contains("\"infinity\"") || minimized.contains("'infinity'"), + "infinity must be quoted, got: {minimized}" + ); + assert!(!looks_like_unquoted_infinity(&minimized)); + let docs = yaml_rust::YamlLoader::load_from_str(&minimized).unwrap(); + assert_eq!(docs.len(), 1); + } + + #[test] + fn package_rules_standard_zip_layout() { + let tmp = tempdir().unwrap(); + let rules = tmp.path().join("Rules"); + let lang = rules.join("Languages").join("en"); + fs::create_dir_all(&lang).unwrap(); + fs::write(rules.join("prefs.yaml"), "- SpeechStyle: [t: \"ClearSpeak\"]\n").unwrap(); + fs::write(lang.join("unicode.yaml"), "- \"+\": [t: \"plus\"]\n").unwrap(); + + let output = tmp.path().join("Rules.zip"); + package_rules(&rules, &output, false, downloadable_compression()).unwrap(); + + let bytes = fs::read(&output).unwrap(); + let names = outer_names(&bytes); + assert!(names.contains(&"Rules/prefs.yaml".to_string())); + assert!(names.contains(&"Rules/Languages/en/en.zip".to_string())); + assert!(!names.iter().any(|n| n.ends_with("unicode.yaml"))); + assert_eq!( + inner_method(&bytes, "Rules/Languages/en/en.zip"), + zip::CompressionMethod::BZIP2 + ); + assert_eq!( + outer_method(&bytes, "Rules/prefs.yaml"), + zip::CompressionMethod::DEFLATE + ); + let unicode = read_nested_zip_text(&bytes, "Rules/Languages/en/en.zip", "unicode.yaml"); + assert_eq!(unicode.replace("\r\n", "\n"), "- \"+\": [t: \"plus\"]\n"); + } + + #[test] + fn should_not_minimize_braille_unicode() { + let tmp = tempdir().unwrap(); + let rules = tmp.path().join("Rules"); + let braille = rules.join("Braille").join("Nemeth"); + fs::create_dir_all(&braille).unwrap(); + let source = "---\n# keep this comment\n - \"1\": [t: \"N⠂\"]\n"; + fs::write(braille.join("unicode.yaml"), source).unwrap(); + + let output = tmp.path().join("Rules-minimized.zip"); + let minimized = package_rules(&rules, &output, true, downloadable_compression()).unwrap(); + assert_eq!(minimized, 0); + + let bytes = fs::read(&output).unwrap(); + let text = read_nested_zip_text(&bytes, "Rules/Braille/Nemeth/Nemeth.zip", "unicode.yaml"); + assert_eq!(text.replace("\r\n", "\n"), source.replace("\r\n", "\n")); + } + + #[test] + fn package_rules_minimized_matches_release_layout() { + let tmp = tempdir().unwrap(); + let rules = tmp.path().join("Rules"); + let lang = rules.join("Languages").join("en"); + fs::create_dir_all(&lang).unwrap(); + fs::write( + lang.join("unicode.yaml"), + "---\n# header comment\n - \"+\": [t: \"plus\"] # trailing\n - \"-\": [t: \"minus\"]\n", + ) + .unwrap(); + fs::write( + lang.join("definitions.yaml"), + "---\n - AdditionalFunctionNames:\n - real=inf\n", + ) + .unwrap(); + + let output = tmp.path().join("Rules-minimized.zip"); + let minimized = package_rules(&rules, &output, true, downloadable_compression()).unwrap(); + assert_eq!(minimized, 1); + + let bytes = fs::read(&output).unwrap(); + let names = outer_names(&bytes); + assert!(names.contains(&"Rules/Languages/en/en.zip".to_string())); + assert!(!names.iter().any(|n| n.ends_with("unicode.yaml"))); + + let unicode = read_nested_zip_text(&bytes, "Rules/Languages/en/en.zip", "unicode.yaml"); + let defs = read_nested_zip_text(&bytes, "Rules/Languages/en/en.zip", "definitions.yaml"); + assert!(!unicode.contains('#')); + assert!(defs.contains("real=inf")); + assert_eq!(yaml_rust::YamlLoader::load_from_str(&unicode).unwrap().len(), 1); + } + + #[test] + fn skips_zz_test_language() { + let tmp = tempdir().unwrap(); + let rules = tmp.path().join("Rules"); + fs::create_dir_all(rules.join("Languages").join("zz")).unwrap(); + fs::write(rules.join("Languages").join("zz").join("unicode.yaml"), "- a: 1\n").unwrap(); + fs::create_dir_all(rules.join("Languages").join("en")).unwrap(); + fs::write(rules.join("Languages").join("en").join("unicode.yaml"), "- b: 2\n").unwrap(); + + let output = tmp.path().join("Rules.zip"); + package_rules(&rules, &output, false, downloadable_compression()).unwrap(); + let names = outer_names(&fs::read(&output).unwrap()); + assert!(names.contains(&"Rules/Languages/en/en.zip".to_string())); + assert!(!names.iter().any(|n| n.contains("/zz"))); + } +} diff --git a/src/interface.rs b/src/interface.rs index 012345056..3177c22c8 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -651,7 +651,8 @@ pub fn get_supported_languages() -> Result> { let mut language_paths = lang_paths.iter() .map(|path| path.strip_prefix(&lang_dir).unwrap() .to_string_lossy() - .replace(std::path::MAIN_SEPARATOR, "-") + // include-zip stores paths with '/'; native trees use MAIN_SEPARATOR. + .replace(['/', '\\'], "-") .to_string()) .filter(|string_path| !string_path.is_empty() ) .collect::>(); diff --git a/src/prefs.rs b/src/prefs.rs index cad23ba6d..0e04414f6 100644 --- a/src/prefs.rs +++ b/src/prefs.rs @@ -26,7 +26,7 @@ use std::cell::RefCell; use std::rc::Rc; use log::{debug, error, warn}; use std::path::{Path, PathBuf}; -use std::sync::LazyLock; +use std::sync::{LazyLock, Mutex}; use crate::speech::{as_str_checked, RulesFor, FileAndTime}; use std::collections::{HashMap, HashSet}; use phf::phf_set; @@ -458,6 +458,14 @@ impl PreferenceManager { /// Unzip the files if needed /// Returns true if it unzipped them pub fn unzip_files(path: &Path, lang: &str, default_lang: Option<&str>) -> Result { + // cargo test runs many threads against the same Rules/ tree. Extracting en.zip + // concurrently can truncate unicode.yaml and yaml-rust then reports "Didn't find rules!". + static UNZIP_LOCK: Mutex<()> = Mutex::new(()); + let _guard = UNZIP_LOCK.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); + return PreferenceManager::unzip_files_locked(path, lang, default_lang); + } + + fn unzip_files_locked(path: &Path, lang: &str, default_lang: Option<&str>) -> Result { thread_local!{ /// when a language/braille code dir is unzipped, it is recorded here static UNZIPPED_FILES: RefCell> = RefCell::new( HashSet::with_capacity(31)); @@ -479,7 +487,7 @@ impl PreferenceManager { // try again in parent dir of regional language let language = lang.split_once('-').unwrap_or((lang, "")).0; // get the parent language // debug!("unzip_files: trying again in parent language: {}", language); - PreferenceManager::unzip_files(path, language, default_lang) + PreferenceManager::unzip_files_locked(path, language, default_lang) .with_context(|| format!("Couldn't open zip file {zip_file_string} in parent {language}: {e}."))? } else { // maybe just regional dialects @@ -488,7 +496,7 @@ impl PreferenceManager { for dir in regional_dirs { // debug!("unzip_files: trying again in subdir: {}", dir.display()); let language = format!("{}-{}", lang, dir.file_name().unwrap().to_str().unwrap()); - if let Ok(result) =PreferenceManager::unzip_files(path, &language, default_lang) { + if let Ok(result) =PreferenceManager::unzip_files_locked(path, &language, default_lang) { return Ok(result); } } @@ -501,11 +509,6 @@ impl PreferenceManager { }; UNZIPPED_FILES.with( |unzipped_files| unzipped_files.borrow_mut().insert(zip_file_string.clone()) ); - // debug!(" unzip_files: unzipped {} files from {}", result, &zip_file_string); - // UNZIPPED_FILES.with( |unzipped_files| { - // debug!("unzip_files: unzipped_files: {:?}", unzipped_files.borrow()); - // }); - return Ok(result); } diff --git a/src/rules_archive.rs b/src/rules_archive.rs new file mode 100644 index 000000000..108220c28 --- /dev/null +++ b/src/rules_archive.rs @@ -0,0 +1,385 @@ +// Shared Rules archive writer used by `build.rs` (include-zip) and the `package-rules` binary. +// +// Layout matches native MathCAT extraction: +// - `Rules/*.yaml` and `Rules/Intent/**` stay loose in the outer archive +// - each `Rules/Languages//` and `Rules/Braille//` directory is stored as `/.zip` +// - `Rules/Languages/zz` (synthetic test language) is omitted +// +// Inner language/braille zips always use BZIP2. Downloadable `Rules.zip` uses DEFLATE for the +// outer archive so Windows Explorer / `unzip` can open it. The include-zip embed uses BZIP2 +// for both layers (MathCAT's `zip` crate reads it; zip 8.x bzip2 is pure Rust and works on wasm). + +use std::fs::{self, File, read_dir}; +use std::io::{self, Cursor, Read, Seek, Write}; +use std::path::{Path, PathBuf}; + +use yaml_rust::{YamlEmitter, YamlLoader}; +use zip::write::{SimpleFileOptions, ZipWriter}; +use zip::CompressionMethod; + +const YAML_SUFFIXES: [&str; 2] = ["yaml", "yml"]; +const MINIMIZE_NAMES: [&str; 2] = ["unicode.yaml", "unicode-full.yaml"]; +const SKIP_LANGUAGE_DIR: &str = "zz"; +const ARCHIVE_ROOT: &str = "Rules"; + +/// Compression methods for nested language zips vs the outer archive. +#[derive(Clone, Copy)] +pub struct ArchiveCompression { + pub inner: CompressionMethod, + pub outer: CompressionMethod, +} + +/// Inner BZIP2 / outer BZIP2 — used for the include-zip embed (only MathCAT reads it). +pub fn include_zip_compression() -> ArchiveCompression { + return ArchiveCompression { + inner: CompressionMethod::BZIP2, + outer: CompressionMethod::BZIP2, + }; +} + +/// Inner BZIP2 / outer DEFLATE — used for downloadable `Rules.zip` / `Rules-minimized.zip`. +pub fn downloadable_compression() -> ArchiveCompression { + return ArchiveCompression { + inner: CompressionMethod::BZIP2, + outer: CompressionMethod::DEFLATE, + }; +} + +fn zip_options(method: CompressionMethod) -> SimpleFileOptions { + return SimpleFileOptions::default() + .compression_method(method) + .compression_level(Some(9)); +} + +fn is_yaml_file(path: &Path) -> bool { + return path + .extension() + .map(|ext| { + let ext = ext.to_ascii_lowercase(); + YAML_SUFFIXES.iter().any(|s| ext == *s) + }) + .unwrap_or(false); +} + +fn is_zip_file(path: &Path) -> bool { + return path.extension().map(|ext| ext.eq_ignore_ascii_case("zip")).unwrap_or(false); +} + +fn posix_path(parts: &[&str]) -> String { + return parts.join("/"); +} + +fn posix_join(prefix: &str, name: &str) -> String { + if prefix.is_empty() { + return name.to_string(); + } + return format!("{}/{}", prefix, name); +} + +fn file_name_str(path: &Path) -> io::Result { + let name = path + .file_name() + .and_then(|n| n.to_str()) + .ok_or_else(|| io::Error::other(format!("non-UTF-8 file name: {}", path.display())))?; + return Ok(name.to_string()); +} + +/// Speech unicode YAML only — braille `unicode.yaml` stays verbatim. +fn should_minimize(path: &Path) -> bool { + let name = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); + if !MINIMIZE_NAMES.contains(&name) { + return false; + } + return path.components().any(|c| c.as_os_str() == "Languages"); +} + +/// Load YAML, drop comments, re-emit. Returns `Err` if yaml-rust cannot parse the file. +pub fn minimize_yaml_text(text: &str) -> Result { + let normalized = text.replace('\t', " "); + let docs = YamlLoader::load_from_str(&normalized).map_err(|e| e.to_string())?; + if docs.is_empty() { + return Ok(String::new()); + } + let mut out = String::new(); + { + let mut emitter = YamlEmitter::new(&mut out); + emitter.compact(true); + for doc in &docs { + emitter.dump(doc).map_err(|e| e.to_string())?; + } + } + if !out.ends_with('\n') { + out.push('\n'); + } + // yaml-rust quotes scalars that parse as f64 (including "infinity"); keep a guard in case + // a future emitter change stops doing that — unquoted `infinity` becomes a float on load. + if looks_like_unquoted_infinity(&out) { + return Err("emitted unquoted 'infinity' (yaml-rust would treat it as a float)".to_string()); + } + return Ok(out); +} + +fn looks_like_unquoted_infinity(yaml: &str) -> bool { + // Match a YAML value `infinity` that is not inside quotes. + for line in yaml.lines() { + let trimmed = line.trim(); + if trimmed == "infinity" || trimmed.ends_with(": infinity") || trimmed.ends_with("- infinity") { + return true; + } + } + return false; +} + +fn minimize_yaml_file(src: &Path, dst: &Path) -> Result<(), String> { + let text = fs::read_to_string(src).map_err(|e| e.to_string())?; + let minimized = minimize_yaml_text(&text)?; + // One document after a successful load+emit — MathCAT requires docs.len() == 1. + let docs = YamlLoader::load_from_str(&minimized).map_err(|e| e.to_string())?; + if docs.len() != 1 { + return Err(format!("minimized YAML is {} documents, expected 1", docs.len())); + } + let _ = docs; + fs::write(dst, minimized).map_err(|e| e.to_string())?; + return Ok(()); +} + +/// Copy a Rules tree. When `minify` is true, rewrite `Languages/**/unicode.yaml` and +/// `unicode-full.yaml` (comments stripped). `Languages/zz` is omitted. Returns the number of +/// files successfully minimized. +pub fn copy_rules_tree(src: &Path, dst: &Path, minify: bool) -> io::Result { + fs::create_dir_all(dst)?; + return copy_dir(src, dst, minify, false); +} + +fn copy_dir(src: &Path, dst: &Path, minify: bool, parent_is_languages: bool) -> io::Result { + let mut minimized = 0usize; + for entry in read_dir(src)? { + let entry = entry?; + let src_path = entry.path(); + let name = file_name_str(&src_path)?; + if parent_is_languages && name == SKIP_LANGUAGE_DIR { + continue; + } + if is_zip_file(&src_path) { + continue; + } + let dst_path = dst.join(&name); + if src_path.is_dir() { + fs::create_dir_all(&dst_path)?; + minimized += copy_dir(&src_path, &dst_path, minify, name == "Languages")?; + } else if minify && should_minimize(&src_path) { + match minimize_yaml_file(&src_path, &dst_path) { + Ok(()) => minimized += 1, + Err(e) => { + eprintln!( + "warning: could not minimize {}; copied verbatim ({})", + src_path.display(), + e + ); + fs::copy(&src_path, &dst_path)?; + } + } + } else { + if let Some(parent) = dst_path.parent() { + fs::create_dir_all(parent)?; + } + fs::copy(&src_path, &dst_path)?; + } + } + return Ok(minimized); +} + +fn add_file_to_zip( + zip: &mut ZipWriter, + in_path: &Path, + zip_name: &str, + options: SimpleFileOptions, +) -> io::Result<()> { + zip.start_file(zip_name, options) + .map_err(io::Error::other)?; + let mut file = File::open(in_path)?; + let mut buffer = Vec::new(); + file.read_to_end(&mut buffer)?; + zip.write_all(&buffer)?; + return Ok(()); +} + +fn add_bytes_to_zip( + zip: &mut ZipWriter, + zip_name: &str, + bytes: &[u8], + options: SimpleFileOptions, +) -> io::Result<()> { + zip.start_file(zip_name, options) + .map_err(io::Error::other)?; + zip.write_all(bytes)?; + return Ok(()); +} + +fn zip_entry( + zip: &mut ZipWriter, + full_path: &Path, + relative_path: &str, + options: SimpleFileOptions, +) -> io::Result { + let mut n_files_in_zip = 0; + if full_path.is_dir() { + for entry in read_dir(full_path)? { + let entry_path = entry?.path(); + if is_zip_file(&entry_path) { + continue; + } + let entry_name = file_name_str(&entry_path)?; + let child_rel = posix_join(relative_path, &entry_name); + n_files_in_zip += zip_entry(zip, &entry_path, &child_rel, options)?; + } + } else if is_yaml_file(full_path) { + add_file_to_zip(zip, full_path, relative_path, options)?; + n_files_in_zip += 1; + } + return Ok(n_files_in_zip); +} + +fn write_inner_zip(source_dir: &Path, inner: SimpleFileOptions) -> io::Result>> { + let cursor = Cursor::new(Vec::new()); + let mut zip = ZipWriter::new(cursor); + let n_files = zip_entry(&mut zip, source_dir, "", inner)?; + let cursor = zip.finish().map_err(io::Error::other)?; + if n_files == 0 { + return Ok(None); + } + return Ok(Some(cursor.into_inner())); +} + +/// Zip each immediate subdirectory of Languages/ or Braille/ into `/.zip`. +fn zip_dir( + rules_section: &Path, + archive_zip: &mut ZipWriter, + inner: SimpleFileOptions, + outer: SimpleFileOptions, + archive_prefix: &str, +) -> io::Result<()> { + if !rules_section.is_dir() { + return Ok(()); + } + for entry in read_dir(rules_section)? { + let entry_path = entry?.path(); + if is_zip_file(&entry_path) { + continue; + } + if entry_path.is_dir() { + let dir_name = file_name_str(&entry_path)?; + if dir_name == SKIP_LANGUAGE_DIR { + continue; + } + if let Some(bytes) = write_inner_zip(&entry_path, inner)? { + let zip_name = posix_path(&[archive_prefix, &dir_name, &format!("{}.zip", dir_name)]); + add_bytes_to_zip(archive_zip, &zip_name, &bytes, outer)?; + } + } else if is_yaml_file(&entry_path) { + // e.g. Rules/Braille/definitions.yaml + let name = file_name_str(&entry_path)?; + let zip_name = posix_path(&[archive_prefix, &name]); + add_file_to_zip(archive_zip, &entry_path, &zip_name, outer)?; + } + } + return Ok(()); +} + +/// Loose YAML at Rules/ and Rules/Intent/ (not zipped per-directory). +fn zip_other_files( + rules_dir: &Path, + archive_zip: &mut ZipWriter, + outer: SimpleFileOptions, + archive_prefix: &str, +) -> io::Result<()> { + for entry in read_dir(rules_dir)? { + let entry_path = entry?.path(); + let entry_name = file_name_str(&entry_path)?; + if entry_path.is_dir() { + if entry_name == "Intent" { + zip_other_files( + &rules_dir.join("Intent"), + archive_zip, + outer, + &posix_join(archive_prefix, "Intent"), + )?; + } + } else if is_yaml_file(&entry_path) { + let zip_name = posix_join(archive_prefix, &entry_name); + add_file_to_zip(archive_zip, &entry_path, &zip_name, outer)?; + } + } + return Ok(()); +} + +/// Write a MathCAT Rules archive from an already-staged tree (`rules_dir` is the `Rules` folder). +pub fn write_rules_archive( + rules_dir: &Path, + output: &Path, + compression: ArchiveCompression, +) -> io::Result<()> { + if let Some(parent) = output.parent() + && !parent.as_os_str().is_empty() + { + fs::create_dir_all(parent)?; + } + let inner = zip_options(compression.inner); + let outer = zip_options(compression.outer); + let archive_zip_file = File::create(output)?; + let mut archive_zip = ZipWriter::new(archive_zip_file); + + zip_other_files(rules_dir, &mut archive_zip, outer, ARCHIVE_ROOT)?; + zip_dir( + &rules_dir.join("Languages"), + &mut archive_zip, + inner, + outer, + &posix_join(ARCHIVE_ROOT, "Languages"), + )?; + zip_dir( + &rules_dir.join("Braille"), + &mut archive_zip, + inner, + outer, + &posix_join(ARCHIVE_ROOT, "Braille"), + )?; + + archive_zip.finish().map_err(io::Error::other)?; + return Ok(()); +} + +/// Copy (optionally minify) `source` and write a downloadable or embeddable Rules archive. +pub fn package_rules( + source: &Path, + output: &Path, + minify: bool, + compression: ArchiveCompression, +) -> io::Result { + if !source.is_dir() { + return Err(io::Error::new( + io::ErrorKind::NotFound, + format!("Rules source directory not found: {}", source.display()), + )); + } + + let staging_root = tempfile_staging_dir()?; + let staged_rules = staging_root.join(ARCHIVE_ROOT); + let minimized = copy_rules_tree(source, &staged_rules, minify)?; + write_rules_archive(&staged_rules, output, compression)?; + let _ = fs::remove_dir_all(&staging_root); + return Ok(minimized); +} + +fn tempfile_staging_dir() -> io::Result { + let base = std::env::temp_dir().join(format!( + "mathcat-rules-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_nanos()) + .unwrap_or(0) + )); + fs::create_dir_all(&base)?; + return Ok(base); +} From 2e2c19cf9532af816d488fa00b8e5cb7a71ce679 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Tue, 18 Aug 2026 00:23:37 -0700 Subject: [PATCH 054/120] Serialize rules unzip and YAML reads during parallel tests Co-authored-by: Cursor --- src/prefs.rs | 6 +----- src/shim_filesystem.rs | 32 +++++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/prefs.rs b/src/prefs.rs index 0e04414f6..2fe69cdf3 100644 --- a/src/prefs.rs +++ b/src/prefs.rs @@ -26,7 +26,7 @@ use std::cell::RefCell; use std::rc::Rc; use log::{debug, error, warn}; use std::path::{Path, PathBuf}; -use std::sync::{LazyLock, Mutex}; +use std::sync::LazyLock; use crate::speech::{as_str_checked, RulesFor, FileAndTime}; use std::collections::{HashMap, HashSet}; use phf::phf_set; @@ -458,10 +458,6 @@ impl PreferenceManager { /// Unzip the files if needed /// Returns true if it unzipped them pub fn unzip_files(path: &Path, lang: &str, default_lang: Option<&str>) -> Result { - // cargo test runs many threads against the same Rules/ tree. Extracting en.zip - // concurrently can truncate unicode.yaml and yaml-rust then reports "Didn't find rules!". - static UNZIP_LOCK: Mutex<()> = Mutex::new(()); - let _guard = UNZIP_LOCK.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); return PreferenceManager::unzip_files_locked(path, lang, default_lang); } diff --git a/src/shim_filesystem.rs b/src/shim_filesystem.rs index 38ff55106..14c5b26fb 100644 --- a/src/shim_filesystem.rs +++ b/src/shim_filesystem.rs @@ -361,15 +361,40 @@ cfg_if! { } } + // Tests extract language/braille zips in parallel. During extraction, individual YAML + // files can briefly exist in a partially-written state (created/truncated before the + // full contents are flushed). Guard both extraction and YAML reads with the same lock + // so readers never observe truncated YAML. + use std::sync::Mutex; + static RULES_ZIP_EXTRACT_LOCK: Mutex<()> = Mutex::new(()); + + fn should_lock_rules_yaml_path(path: &Path) -> bool { + let s = path.to_string_lossy(); + let is_yaml = s.ends_with(".yaml") || s.ends_with(".yml"); + if !is_yaml { + return false; + } + return s.contains("Rules/Languages/") || s.contains("Rules\\Languages\\") + || s.contains("Rules/Braille/") || s.contains("Rules\\Braille\\"); + } + pub fn read_to_string_shim(path: &Path) -> Result { let path = match path.canonicalize() { Ok(path) => path, Err(_) => path.to_path_buf(), }; debug!("Reading file '{}'", path.display()); - match std::fs::read_to_string(&path) { - Ok(str) => return Ok(str), - Err(e) => bail!("Read error while trying to read {}: {}", path.display(), e), + if should_lock_rules_yaml_path(&path) { + let _guard = RULES_ZIP_EXTRACT_LOCK.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); + match std::fs::read_to_string(&path) { + Ok(str) => return Ok(str), + Err(e) => bail!("Read error while trying to read {}: {}", path.display(), e), + } + } else { + match std::fs::read_to_string(&path) { + Ok(str) => return Ok(str), + Err(e) => bail!("Read error while trying to read {}: {}", path.display(), e), + } } } @@ -386,6 +411,7 @@ cfg_if! { } }, Ok(contents) => { + let _guard = RULES_ZIP_EXTRACT_LOCK.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); let archive = std::io::Cursor::new(contents); let mut zip_archive = zip::ZipArchive::new(archive).unwrap(); zip_archive.extract(dir).expect("Zip extraction failed"); From 5e899d7d66a56d0068a80c09a95c81e445fbadeb Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Tue, 18 Aug 2026 11:54:44 -0700 Subject: [PATCH 055/120] update version number --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 32e83cf27..67522e0b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mathcat" -version = "0.7.6-rc.1" +version = "0.7.7-alpha.1" authors = ["Neil Soiffer "] license = "MIT" description = "MathCAT: Math Capable Assistive Technology ('Speech and braille from MathML')" From 04882d3fa42ebb4430b8245ba1a9101e6d41a784 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Tue, 18 Aug 2026 11:55:29 -0700 Subject: [PATCH 056/120] better speech for missing char used for parsing --- Rules/Languages/en/unicode-full.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rules/Languages/en/unicode-full.yaml b/Rules/Languages/en/unicode-full.yaml index 15c169d6a..8d66bbf9a 100644 --- a/Rules/Languages/en/unicode-full.yaml +++ b/Rules/Languages/en/unicode-full.yaml @@ -3777,7 +3777,7 @@ - "→": [t: "right arrow"] # 0xffeb - "↓": [t: "down arrow"] # 0xffec - "": [t: "unknown or missing object"] # 0xfffc - - "�": [t: "unknown or missing character"] # 0xfffd + - "�": [t: "missing char"] # 0xfffd (used for missing input when entering braille) - "🣑": [t: "is in equilibrium with"] # 0x1F8D1 - "🣒": [t: "is in equilibrium biased to the right with"] # 0x1F8D2 From 25b472b60ee5a9d6340e7e9ef987c58b28f01fb9 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Tue, 18 Aug 2026 12:14:02 -0700 Subject: [PATCH 057/120] udate version # --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 67522e0b3..32e83cf27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mathcat" -version = "0.7.7-alpha.1" +version = "0.7.6-rc.1" authors = ["Neil Soiffer "] license = "MIT" description = "MathCAT: Math Capable Assistive Technology ('Speech and braille from MathML')" From 67cc5462e7b96b176dbd0544e308b23b8ce3c937 Mon Sep 17 00:00:00 2001 From: Cristian Consonni Date: Wed, 19 Aug 2026 02:03:39 +0200 Subject: [PATCH 058/120] Add option --exclude to audit-translations --- PythonScripts/audit_translations/README.md | 11 ++++++- PythonScripts/audit_translations/auditor.py | 34 +++++++++++++-------- PythonScripts/audit_translations/cli.py | 14 ++++++++- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/PythonScripts/audit_translations/README.md b/PythonScripts/audit_translations/README.md index 8aedf1265..fd399e412 100644 --- a/PythonScripts/audit_translations/README.md +++ b/PythonScripts/audit_translations/README.md @@ -77,6 +77,7 @@ uv run --project PythonScripts audit-translations --list * Region variants are shown as `lang-region` (e.g., `zz-aa`) based on subdirectories under `Rules/Languages/`. * `--source`: Sets the source/reference language. Defaults to `en`. * `--file`: Audits a single specific file instead of the whole directory. +* `--exclude`: Exclude one or more files from the audit. * `--rules-dir`: Override the Rules/Languages directory path. * `--only`: Filter issue types (comma-separated): `missing`, `untranslated`, `extra`, `diffs`, `all`. * `--verbose`: Show detailed output including source/target snippets for rule differences. @@ -100,9 +101,17 @@ uv run audit-translations de # Compare Norwegian Bokmal against Swedish instead of English uv run audit-translations nb --source sv -# Audit only a specific file +# Audit only a specific file (note: --file is incompatible with --exclude) +uv run audit-translations es --file ClearSpeak_Rules.yaml uv run audit-translations es --file SharedRules/default.yaml +# Exclude a list of files from the audit, note that if you use this option before specifying the +# target language you'll need to use the option terminator (--). +# (note: --exclude is incompatible with --file) +uv run audit-translations es --exclude unicode-full.yaml +uv run audit-translations es --exclude unicode-full.yaml unicode.yaml +uv run audit-translations --exclude unicode-full.yaml -- es + # Audit a regional variant (merges Rules/Languages/de and Rules/Languages/de/CH) uv run audit-translations de-CH diff --git a/PythonScripts/audit_translations/auditor.py b/PythonScripts/audit_translations/auditor.py index b67cbf911..37ec44f13 100644 --- a/PythonScripts/audit_translations/auditor.py +++ b/PythonScripts/audit_translations/auditor.py @@ -46,24 +46,31 @@ def is_definitions_file(file_path: str | Path) -> bool: return Path(file_path).name == "definitions.yaml" -def get_yaml_files(lang_dir: Path, region_dir: Path | None = None) -> list[Path]: +def get_yaml_files(lang_dir: Path, region_dir: Path | None = None, excluded_files: list[str] | None = None) -> list[Path]: """Get all YAML files to audit for a language, including region overrides.""" files: set[Path] = set() - def collect_from(directory: Path, root: Path) -> None: + def collect_from(directory: Path, root: Path, excluded: list[str] | None) -> None: if not directory.exists(): return - for f in directory.glob("*.yaml"): - if f.name != "prefs.yaml": - files.add(f.relative_to(root)) - shared_dir = directory / "SharedRules" - if shared_dir.exists(): - for f in shared_dir.glob("*.yaml"): - files.add(f.relative_to(root)) - - collect_from(lang_dir, lang_dir) + + excluded_paths = {Path(path) for path in excluded or []} + + candidates = {f for f in directory.glob("*.yaml") if f.name != "prefs.yaml"} + candidates.update((directory / "SharedRules").glob("*.yaml")) + + relative_candidates = {f.relative_to(directory): f for f in candidates} + + for path in excluded_paths - relative_candidates.keys(): + console.print(f"\n[yellow]⚠ Warning:[/] File to exclude {path} does not exist.") + + files.update( + f.relative_to(root) for relative_path, f in relative_candidates.items() if relative_path not in excluded_paths + ) + + collect_from(lang_dir, lang_dir, excluded_files) if region_dir: - collect_from(region_dir, region_dir) + collect_from(region_dir, region_dir, excluded_files) return sorted(files) @@ -215,6 +222,7 @@ def merge_definitions( def audit_language( language: str, specific_file: str | None = None, + excluded_files: list[str] | None = None, rules_dir: str | None = None, issue_filter: set[str] | None = None, verbose: bool = False, @@ -248,7 +256,7 @@ def audit_language( raise AuditError(f"Target region directory not found: {translated_region_dir}") # Get list of files to audit - files = [specific_file] if specific_file else get_yaml_files(source_dir, source_region_dir) + files = [specific_file] if specific_file else get_yaml_files(source_dir, source_region_dir, excluded_files) print_audit_header(language, len(files), source_language) diff --git a/PythonScripts/audit_translations/cli.py b/PythonScripts/audit_translations/cli.py index 6916a636e..5bdf30e6d 100644 --- a/PythonScripts/audit_translations/cli.py +++ b/PythonScripts/audit_translations/cli.py @@ -30,7 +30,18 @@ def main() -> None: parser.add_argument("language", nargs="?", help="Language code to audit (e.g., 'es', 'de', 'fi')") parser.add_argument("--source", default="en", help="Source/reference language code (default: 'en')") - parser.add_argument("--file", dest="specific_file", help="Audit only a specific file (e.g., 'SharedRules/default.yaml')") + file_group = parser.add_mutually_exclusive_group() + file_group.add_argument( + "--file", + dest="specific_file", + help="Audit only a specific file (e.g., 'SharedRules/default.yaml')", + ) + file_group.add_argument( + "--exclude", + nargs="+", + dest="excluded_files", + help="Exclude a list of files from the audit.", + ) parser.add_argument("--list", action="store_true", help="List available languages") parser.add_argument("--rules-dir", help="Override Rules/Languages directory path") parser.add_argument( @@ -67,6 +78,7 @@ def main() -> None: audit_language( args.language, args.specific_file, + args.excluded_files, args.rules_dir, issue_filter, args.verbose, From 3d267a07461db2e597f03ee22d1e936c45e1e522 Mon Sep 17 00:00:00 2001 From: Cristian Consonni Date: Wed, 19 Aug 2026 03:21:14 +0200 Subject: [PATCH 059/120] Add tests for --exclude option --- .../tests/test_cli_end_to_end.py | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/PythonScripts/audit_translations/tests/test_cli_end_to_end.py b/PythonScripts/audit_translations/tests/test_cli_end_to_end.py index 7876bc0de..4ef5ee9de 100644 --- a/PythonScripts/audit_translations/tests/test_cli_end_to_end.py +++ b/PythonScripts/audit_translations/tests/test_cli_end_to_end.py @@ -127,6 +127,79 @@ def test_cli_main_rich_only_filters_issue_groups(capsys, monkeypatch) -> None: assert "Structure Differences" not in output +@pytest.mark.parametrize( + ("excluded_file", "expected_file", "expected_rule", "unexpected_rule"), + [ + ( + "default.yaml", + "SharedRules/default.yaml", + "shared-only", + "root-only", + ), + ( + "SharedRules/default.yaml", + "default.yaml", + "root-only", + "shared-only", + ), + ], +) +def test_cli_main_exclude_uses_relative_paths( + tmp_path, + capsys, + monkeypatch, + excluded_file, + expected_file, + expected_rule, + unexpected_rule, +) -> None: + """Ensure --exclude distinguishes root files from SharedRules files.""" + rules_dir = tmp_path / "Rules" / "Languages" + source_dir = rules_dir / "en" + target_dir = rules_dir / "de" + + (source_dir / "SharedRules").mkdir(parents=True) + (target_dir / "SharedRules").mkdir(parents=True) + + (source_dir / "default.yaml").write_text( + '- name: root-only\n tag: mo\n match: ".//m:mi"\n replace:\n - t: "root"\n', + encoding="utf-8", + ) + (source_dir / "SharedRules" / "default.yaml").write_text( + '- name: shared-only\n tag: mo\n match: ".//m:mi"\n replace:\n - t: "shared"\n', + encoding="utf-8", + ) + + target_rule = '- name: target-only\n tag: mo\n match: ".//m:mi"\n replace:\n - t: "target"\n' + (target_dir / "default.yaml").write_text( + target_rule, + encoding="utf-8", + ) + (target_dir / "SharedRules" / "default.yaml").write_text( + target_rule, + encoding="utf-8", + ) + + args = [ + "de", + "--rules-dir", + str(rules_dir), + "--exclude", + excluded_file, + "--only", + "missing", + ] + monkeypatch.setattr(sys, "argv", ["audit_translations", *args]) + + audit_cli.main() + output = strip_ansi(capsys.readouterr().out) + + assert "Files to check: 1" in output + assert expected_file in output + assert expected_rule in output + assert unexpected_rule not in output + + def test_cli_main_accepts_source_language(capsys, monkeypatch) -> None: """ Ensure --source changes the reference language without changing target semantics. From 0f4791ccd674d4c0ea78a1576140e09ae1c33315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Gro=C3=9F?= Date: Wed, 19 Aug 2026 08:39:00 +0200 Subject: [PATCH 060/120] Add test case IDs for CLI main exclude tests --- PythonScripts/audit_translations/tests/test_cli_end_to_end.py | 1 + 1 file changed, 1 insertion(+) diff --git a/PythonScripts/audit_translations/tests/test_cli_end_to_end.py b/PythonScripts/audit_translations/tests/test_cli_end_to_end.py index 4ef5ee9de..40cf3f8b2 100644 --- a/PythonScripts/audit_translations/tests/test_cli_end_to_end.py +++ b/PythonScripts/audit_translations/tests/test_cli_end_to_end.py @@ -143,6 +143,7 @@ def test_cli_main_rich_only_filters_issue_groups(capsys, monkeypatch) -> None: "shared-only", ), ], + ids=["exclude-root", "exclude-shared-rules"], ) def test_cli_main_exclude_uses_relative_paths( tmp_path, From 512919a3c65f183bd568e43d1db8825f8c1e6107 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Wed, 19 Aug 2026 17:54:43 -0700 Subject: [PATCH 061/120] fix minimized zip file so that flow style is used to reduce space --- AGENTS.md | 2 +- src/bin/package_rules.rs | 91 +++++++++++++++++++-- src/rules_archive.rs | 167 ++++++++++++++++++++++++++++++++------- 3 files changed, 226 insertions(+), 34 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 76ea6a889..c6ca9e756 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,7 @@ but add common mistakes of AI agents here instead. - `ClearSpeak_Rules.yaml`, `SimpleSpeak_Rules.yaml` - `SharedRules/`, `unicode.yaml`, `unicode-full.yaml`, `definitions.yaml`, `navigate.yaml` - `build.rs` can bundle rules into `rules.zip` when `include-zip` is enabled (always the minimized tree). Shared zipper: `src/rules_archive.rs`, invoked as `cargo run --bin package-rules -- Rules [--minimize]`. -- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (comments stripped from `Languages/**/unicode.yaml` and `unicode-full.yaml` only). Each language/braille subdir is stored as `/.zip`; top-level and `Intent/` YAML stay loose. Inner language zips use BZIP2; the outer downloadable archive uses DEFLATE so `unzip` works. Both omit `Languages/zz`; the `test-rules-package` job restores `zz` from git after unzip so unit tests still run against release-like archives. +- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style rewrite of all YAML: comments dropped). Each language/braille subdir is stored as `/.zip`; top-level and `Intent/` YAML stay loose. Inner language zips use BZIP2; the outer downloadable archive uses DEFLATE so `unzip` works. Both omit `Languages/zz`; the `test-rules-package` job restores `zz` from git after unzip so unit tests still run against release-like archives. ## Translation Conventions - `t:` means untranslated or unverified. diff --git a/src/bin/package_rules.rs b/src/bin/package_rules.rs index 261059bea..900af235f 100644 --- a/src/bin/package_rules.rs +++ b/src/bin/package_rules.rs @@ -15,14 +15,14 @@ use std::process::ExitCode; #[command( name = "package-rules", about = "Package MathCAT Rules/ into a zip archive. \ - Use --minimize to strip comments from Languages unicode YAML." + Use --minimize to rewrite all Rules YAML in compact flow style." )] struct Options { /// Path to the Rules directory source: PathBuf, /// Output zip file path (e.g. Rules.zip) output: PathBuf, - /// Emit compact unicode.yaml / unicode-full.yaml without comments (speech languages only) + /// Rewrite all Rules YAML as compact flow YAML without comments #[arg(long)] minimize: bool, } @@ -119,6 +119,23 @@ mod tests { assert_eq!(docs.len(), 1); } + #[test] + fn minimize_yaml_uses_flow_style() { + let source = "---\n# comment\n - \"+\": [t: \"plus\"] # inline\n - \"-\": [t: \"minus\"]\n"; + let minimized = minimize_yaml_text(source).unwrap(); + assert!( + minimized.contains('[') && minimized.contains('{'), + "expected flow collections, got: {minimized}" + ); + assert!( + !minimized.lines().any(|line| line.trim_start().starts_with("- ")), + "expected no block-sequence dashes, got: {minimized}" + ); + let original = yaml_rust::YamlLoader::load_from_str(source).unwrap(); + let roundtrip = yaml_rust::YamlLoader::load_from_str(&minimized).unwrap(); + assert_eq!(original, roundtrip); + } + #[test] fn minimize_quotes_infinity() { let source = "---\n - \"∞\": [t: \"infinity\"]\n"; @@ -132,6 +149,22 @@ mod tests { assert_eq!(docs.len(), 1); } + #[test] + fn minimize_roundtrips_en_unicode_files() { + for name in ["unicode.yaml", "unicode-full.yaml"] { + let path = std::path::Path::new("Rules/Languages/en").join(name); + let source = fs::read_to_string(&path).expect(name); + let minimized = minimize_yaml_text(&source).unwrap(); + assert!( + !minimized.lines().any(|line| line.trim_start().starts_with("- ")), + "{name} still uses block sequences" + ); + let original = yaml_rust::YamlLoader::load_from_str(&source).unwrap(); + let roundtrip = yaml_rust::YamlLoader::load_from_str(&minimized).unwrap(); + assert_eq!(original, roundtrip, "{name} did not round-trip"); + } + } + #[test] fn package_rules_standard_zip_layout() { let tmp = tempdir().unwrap(); @@ -161,8 +194,43 @@ mod tests { assert_eq!(unicode.replace("\r\n", "\n"), "- \"+\": [t: \"plus\"]\n"); } + fn walk_yaml_files(dir: &std::path::Path, out: &mut Vec) { + for entry in fs::read_dir(dir).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + if path.is_dir() { + walk_yaml_files(&path, out); + } else if is_yaml_file(&path) { + out.push(path); + } + } + } + #[test] - fn should_not_minimize_braille_unicode() { + fn minimize_roundtrips_all_rules_yaml() { + let mut paths = Vec::new(); + walk_yaml_files(std::path::Path::new("Rules"), &mut paths); + assert!(!paths.is_empty()); + for path in paths { + if path.components().any(|c| c.as_os_str() == SKIP_LANGUAGE_DIR) { + continue; + } + let source = fs::read_to_string(&path).unwrap_or_else(|e| panic!("{}: {e}", path.display())); + let minimized = minimize_yaml_text(&source) + .unwrap_or_else(|e| panic!("{}: {e}", path.display())); + assert!( + !minimized.lines().any(|line| line.trim_start().starts_with("- ")), + "{} still uses block sequences", + path.display() + ); + let original = yaml_rust::YamlLoader::load_from_str(&source).unwrap(); + let roundtrip = yaml_rust::YamlLoader::load_from_str(&minimized).unwrap(); + assert_eq!(original, roundtrip, "{} did not round-trip", path.display()); + } + } + + #[test] + fn minimize_braille_unicode() { let tmp = tempdir().unwrap(); let rules = tmp.path().join("Rules"); let braille = rules.join("Braille").join("Nemeth"); @@ -172,11 +240,16 @@ mod tests { let output = tmp.path().join("Rules-minimized.zip"); let minimized = package_rules(&rules, &output, true, downloadable_compression()).unwrap(); - assert_eq!(minimized, 0); + assert_eq!(minimized, 1); let bytes = fs::read(&output).unwrap(); let text = read_nested_zip_text(&bytes, "Rules/Braille/Nemeth/Nemeth.zip", "unicode.yaml"); - assert_eq!(text.replace("\r\n", "\n"), source.replace("\r\n", "\n")); + assert!(!text.contains('#')); + assert!(text.contains('[') && text.contains('{')); + assert!(!text.lines().any(|line| line.trim_start().starts_with("- "))); + let original = yaml_rust::YamlLoader::load_from_str(source).unwrap(); + let roundtrip = yaml_rust::YamlLoader::load_from_str(&text).unwrap(); + assert_eq!(original, roundtrip); } #[test] @@ -198,7 +271,7 @@ mod tests { let output = tmp.path().join("Rules-minimized.zip"); let minimized = package_rules(&rules, &output, true, downloadable_compression()).unwrap(); - assert_eq!(minimized, 1); + assert_eq!(minimized, 2); let bytes = fs::read(&output).unwrap(); let names = outer_names(&bytes); @@ -208,8 +281,14 @@ mod tests { let unicode = read_nested_zip_text(&bytes, "Rules/Languages/en/en.zip", "unicode.yaml"); let defs = read_nested_zip_text(&bytes, "Rules/Languages/en/en.zip", "definitions.yaml"); assert!(!unicode.contains('#')); + assert!(unicode.contains('[') && unicode.contains('{')); + assert!(!unicode.lines().any(|line| line.trim_start().starts_with("- "))); + assert!(!defs.contains('#')); + assert!(defs.contains('[') && defs.contains('{')); + assert!(!defs.lines().any(|line| line.trim_start().starts_with("- "))); assert!(defs.contains("real=inf")); assert_eq!(yaml_rust::YamlLoader::load_from_str(&unicode).unwrap().len(), 1); + assert_eq!(yaml_rust::YamlLoader::load_from_str(&defs).unwrap().len(), 1); } #[test] diff --git a/src/rules_archive.rs b/src/rules_archive.rs index 108220c28..70d8d81fe 100644 --- a/src/rules_archive.rs +++ b/src/rules_archive.rs @@ -13,12 +13,11 @@ use std::fs::{self, File, read_dir}; use std::io::{self, Cursor, Read, Seek, Write}; use std::path::{Path, PathBuf}; -use yaml_rust::{YamlEmitter, YamlLoader}; +use yaml_rust::{Yaml, YamlLoader}; use zip::write::{SimpleFileOptions, ZipWriter}; use zip::CompressionMethod; const YAML_SUFFIXES: [&str; 2] = ["yaml", "yml"]; -const MINIMIZE_NAMES: [&str; 2] = ["unicode.yaml", "unicode-full.yaml"]; const SKIP_LANGUAGE_DIR: &str = "zz"; const ARCHIVE_ROOT: &str = "Rules"; @@ -83,17 +82,7 @@ fn file_name_str(path: &Path) -> io::Result { .ok_or_else(|| io::Error::other(format!("non-UTF-8 file name: {}", path.display())))?; return Ok(name.to_string()); } - -/// Speech unicode YAML only — braille `unicode.yaml` stays verbatim. -fn should_minimize(path: &Path) -> bool { - let name = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); - if !MINIMIZE_NAMES.contains(&name) { - return false; - } - return path.components().any(|c| c.as_os_str() == "Languages"); -} - -/// Load YAML, drop comments, re-emit. Returns `Err` if yaml-rust cannot parse the file. +/// Load YAML, drop comments, re-emit in flow style. Returns `Err` if yaml-rust cannot parse the file. pub fn minimize_yaml_text(text: &str) -> Result { let normalized = text.replace('\t', " "); let docs = YamlLoader::load_from_str(&normalized).map_err(|e| e.to_string())?; @@ -101,16 +90,13 @@ pub fn minimize_yaml_text(text: &str) -> Result { return Ok(String::new()); } let mut out = String::new(); - { - let mut emitter = YamlEmitter::new(&mut out); - emitter.compact(true); - for doc in &docs { - emitter.dump(doc).map_err(|e| e.to_string())?; + for (i, doc) in docs.iter().enumerate() { + if i > 0 { + out.push_str("\n---\n"); } + emit_flow(&mut out, doc)?; } - if !out.ends_with('\n') { - out.push('\n'); - } + out.push('\n'); // yaml-rust quotes scalars that parse as f64 (including "infinity"); keep a guard in case // a future emitter change stops doing that — unquoted `infinity` becomes a float on load. if looks_like_unquoted_infinity(&out) { @@ -119,15 +105,143 @@ pub fn minimize_yaml_text(text: &str) -> Result { return Ok(out); } +fn emit_flow(out: &mut String, yaml: &Yaml) -> Result<(), String> { + match yaml { + Yaml::Array(items) => { + out.push('['); + for (i, item) in items.iter().enumerate() { + if i > 0 { + out.push(','); + } + emit_flow(out, item)?; + } + out.push(']'); + } + Yaml::Hash(hash) => { + out.push('{'); + for (i, (key, value)) in hash.iter().enumerate() { + if i > 0 { + out.push(','); + } + emit_flow(out, key)?; + out.push_str(": "); + emit_flow(out, value)?; + } + out.push('}'); + } + Yaml::String(s) => emit_yaml_string(out, s), + Yaml::Boolean(true) => out.push_str("true"), + Yaml::Boolean(false) => out.push_str("false"), + Yaml::Integer(i) => out.push_str(&i.to_string()), + Yaml::Real(s) => out.push_str(s), + Yaml::Null | Yaml::BadValue => out.push('~'), + Yaml::Alias(_) => return Err("cannot minimize YAML that contains aliases".to_string()), + } + return Ok(()); +} + +fn emit_yaml_string(out: &mut String, s: &str) { + if need_quotes(s) { + escape_yaml_string(out, s); + } else { + out.push_str(s); + } +} + +/// Same quoting rules as yaml-rust 0.4's emitter, so load/emit round-trips. +fn need_quotes(string: &str) -> bool { + string.is_empty() + || string.starts_with(' ') + || string.ends_with(' ') + || string.starts_with(|character: char| { + matches!(character, '&' | '*' | '?' | '|' | '-' | '<' | '>' | '=' | '!' | '%' | '@') + }) + || string.contains(|character: char| { + matches!( + character, + ':' | '{' | '}' | '[' | ']' | ',' | '#' | '`' | '"' | '\'' | '\\' + | '\0'..='\x06' + | '\t' + | '\n' + | '\r' + | '\x0e'..='\x1a' + | '\x1c'..='\x1f' + ) + }) + || [ + "yes", "Yes", "YES", "no", "No", "NO", "True", "TRUE", "true", "False", "FALSE", + "false", "on", "On", "ON", "off", "Off", "OFF", "null", "Null", "NULL", "~", + ] + .contains(&string) + || string.starts_with('.') + || string.starts_with("0x") + || string.parse::().is_ok() + || string.parse::().is_ok() +} + +fn escape_yaml_string(out: &mut String, v: &str) { + out.push('"'); + let mut start = 0; + for (i, byte) in v.bytes().enumerate() { + let escaped = match byte { + b'"' => "\\\"", + b'\\' => "\\\\", + b'\x00' => "\\u0000", + b'\x01' => "\\u0001", + b'\x02' => "\\u0002", + b'\x03' => "\\u0003", + b'\x04' => "\\u0004", + b'\x05' => "\\u0005", + b'\x06' => "\\u0006", + b'\x07' => "\\u0007", + b'\x08' => "\\b", + b'\t' => "\\t", + b'\n' => "\\n", + b'\x0b' => "\\u000b", + b'\x0c' => "\\f", + b'\r' => "\\r", + b'\x0e' => "\\u000e", + b'\x0f' => "\\u000f", + b'\x10' => "\\u0010", + b'\x11' => "\\u0011", + b'\x12' => "\\u0012", + b'\x13' => "\\u0013", + b'\x14' => "\\u0014", + b'\x15' => "\\u0015", + b'\x16' => "\\u0016", + b'\x17' => "\\u0017", + b'\x18' => "\\u0018", + b'\x19' => "\\u0019", + b'\x1a' => "\\u001a", + b'\x1b' => "\\u001b", + b'\x1c' => "\\u001c", + b'\x1d' => "\\u001d", + b'\x1e' => "\\u001e", + b'\x1f' => "\\u001f", + b'\x7f' => "\\u007f", + _ => continue, + }; + if start < i { + out.push_str(&v[start..i]); + } + out.push_str(escaped); + start = i + 1; + } + if start != v.len() { + out.push_str(&v[start..]); + } + out.push('"'); +} + fn looks_like_unquoted_infinity(yaml: &str) -> bool { - // Match a YAML value `infinity` that is not inside quotes. + // Block-style leftovers and flow mappings (`: infinity}` / `: infinity,`). for line in yaml.lines() { let trimmed = line.trim(); if trimmed == "infinity" || trimmed.ends_with(": infinity") || trimmed.ends_with("- infinity") { return true; } } - return false; + return yaml.contains(": infinity,") || yaml.contains(": infinity}") || yaml.contains(": infinity]"); } fn minimize_yaml_file(src: &Path, dst: &Path) -> Result<(), String> { @@ -143,9 +257,8 @@ fn minimize_yaml_file(src: &Path, dst: &Path) -> Result<(), String> { return Ok(()); } -/// Copy a Rules tree. When `minify` is true, rewrite `Languages/**/unicode.yaml` and -/// `unicode-full.yaml` (comments stripped). `Languages/zz` is omitted. Returns the number of -/// files successfully minimized. +/// Copy a Rules tree. When `minify` is true, rewrite every YAML file as comment-free flow YAML. +/// `Languages/zz` is omitted. Returns the number of files successfully minimized. pub fn copy_rules_tree(src: &Path, dst: &Path, minify: bool) -> io::Result { fs::create_dir_all(dst)?; return copy_dir(src, dst, minify, false); @@ -167,7 +280,7 @@ fn copy_dir(src: &Path, dst: &Path, minify: bool, parent_is_languages: bool) -> if src_path.is_dir() { fs::create_dir_all(&dst_path)?; minimized += copy_dir(&src_path, &dst_path, minify, name == "Languages")?; - } else if minify && should_minimize(&src_path) { + } else if minify && is_yaml_file(&src_path) { match minimize_yaml_file(&src_path, &dst_path) { Ok(()) => minimized += 1, Err(e) => { From 88856c9f8c72c69174f762ce23b07afcff421c48 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Wed, 19 Aug 2026 23:21:21 -0700 Subject: [PATCH 062/120] fix test that depending on the details of YAML notation -- broke for compact flow notation --- src/prefs.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/prefs.rs b/src/prefs.rs index 2fe69cdf3..4dc51c4bc 100644 --- a/src/prefs.rs +++ b/src/prefs.rs @@ -1288,8 +1288,11 @@ cfg_if::cfg_if! {if #[cfg(not(feature = "include-zip"))] { let braille_before = interface::get_braille("").unwrap(); let unicode_file = temp_rules_dir.join("Braille/Nemeth/unicode.yaml"); let unicode_contents = fs::read_to_string(&unicode_file).unwrap(); - // remap the digit '1' to the (distinctive) full braille cell '⠿' - let changed_unicode = unicode_contents.replace("\"1\": [t: \"N⠂\"]", "\"1\": [t: \"N⠿\"]"); + // Remap digit '1' to the distinctive full braille cell '⠿'. + // Source Rules use block style (`"1": [t: "N⠂"]`); Rules-minimized.zip uses flow (`{"1": [{t: N⠂}]}`). + let changed_unicode = unicode_contents + .replace("\"1\": [t: \"N⠂\"]", "\"1\": [t: \"N⠿\"]") + .replace("{\"1\": [{t: N⠂}]}", "{\"1\": [{t: N⠿}]}"); assert_ne!(changed_unicode, unicode_contents, "expected to find the Nemeth mapping for '1'"); fs::write(&unicode_file, changed_unicode).unwrap(); sleep(Duration::from_millis(5)); From 728bbb71d154ae6b7de43954db5e5dd0f0f9b577 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 20 Aug 2026 00:31:15 -0700 Subject: [PATCH 063/120] fix row terminator for UEB in mtable --- Rules/Braille/UEB/UEB_Rules.yaml | 3 +-- tests/braille/UEB/iceb.rs | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/Rules/Braille/UEB/UEB_Rules.yaml b/Rules/Braille/UEB/UEB_Rules.yaml index 8e3491c80..04c63947d 100644 --- a/Rules/Braille/UEB/UEB_Rules.yaml +++ b/Rules/Braille/UEB/UEB_Rules.yaml @@ -159,8 +159,7 @@ replace: - test: if: "../*[2]" # more than one row - then: [t: "⠠"] - - t: "" + then: [t: "⠸W"] - x: $RowStart - test: if: .[self::m:mlabeledtr] diff --git a/tests/braille/UEB/iceb.rs b/tests/braille/UEB/iceb.rs index 731e136b1..2c7c9a5c7 100644 --- a/tests/braille/UEB/iceb.rs +++ b/tests/braille/UEB/iceb.rs @@ -1465,6 +1465,49 @@ fn binomial_14_3_3_2_mtable() -> Result<()> { } +#[test] +fn matrix_15_2_1() -> Result<()> { + // GTM 15.2 first example: I = [[1,0],[0,1]] with enlarged parentheses (one-line encoding). + let expr = r#"( + 10 + 01 + )"#; + test_braille("UEB", expr, "⠸⠀⠐⠣⠼⠁⠀⠼⠚⠠⠐⠜⠸⠀⠐⠣⠼⠚⠀⠼⠁⠠⠐⠜")?; + return Ok(()); +} + +#[test] +fn matrix_15_2_2() -> Result<()> { + // GTM 15.2: matrix with enlarged square brackets. + let expr = r#"[ + ab + cd + ]"#; + test_braille("UEB", expr, "⠸⠀⠨⠣⠁⠀⠃⠠⠨⠜⠸⠀⠨⠣⠰⠉⠀⠙⠠⠨⠜")?; + return Ok(()); +} + +#[test] +fn matrix_15_2_3() -> Result<()> { + // Single-row: no enlarged (dot-6) markers (UEB_Rules one-row path). + let expr = r#"( + 123 + )"#; + test_braille("UEB", expr, "⠐⠣⠼⠁⠀⠼⠃⠀⠼⠉⠐⠜")?; + return Ok(()); +} + +#[test] +fn determinant_15_3_1() -> Result<()> { + // GTM 15.3: determinant with enlarged vertical bars. + let expr = r#"| + 12 + 34 + |"#; + test_braille("UEB", expr, "⠸⠀⠸⠳⠼⠁⠀⠼⠃⠠⠸⠳⠸⠀⠸⠳⠼⠉⠀⠼⠙⠠⠸⠳")?; + return Ok(()); +} + #[test] fn chem_16_2_8() -> Result<()> { let expr = "Ca(OH)2"; From 4a7f5ca0e926d06606043ddd24301b9b74a91d86 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 20 Aug 2026 00:31:15 -0700 Subject: [PATCH 064/120] fix row terminator for UEB in mtable --- Rules/Braille/UEB/UEB_Rules.yaml | 3 +-- tests/braille/UEB/iceb.rs | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/Rules/Braille/UEB/UEB_Rules.yaml b/Rules/Braille/UEB/UEB_Rules.yaml index 8e3491c80..04c63947d 100644 --- a/Rules/Braille/UEB/UEB_Rules.yaml +++ b/Rules/Braille/UEB/UEB_Rules.yaml @@ -159,8 +159,7 @@ replace: - test: if: "../*[2]" # more than one row - then: [t: "⠠"] - - t: "" + then: [t: "⠸W"] - x: $RowStart - test: if: .[self::m:mlabeledtr] diff --git a/tests/braille/UEB/iceb.rs b/tests/braille/UEB/iceb.rs index 731e136b1..2c7c9a5c7 100644 --- a/tests/braille/UEB/iceb.rs +++ b/tests/braille/UEB/iceb.rs @@ -1465,6 +1465,49 @@ fn binomial_14_3_3_2_mtable() -> Result<()> { } +#[test] +fn matrix_15_2_1() -> Result<()> { + // GTM 15.2 first example: I = [[1,0],[0,1]] with enlarged parentheses (one-line encoding). + let expr = r#"( + 10 + 01 + )"#; + test_braille("UEB", expr, "⠸⠀⠐⠣⠼⠁⠀⠼⠚⠠⠐⠜⠸⠀⠐⠣⠼⠚⠀⠼⠁⠠⠐⠜")?; + return Ok(()); +} + +#[test] +fn matrix_15_2_2() -> Result<()> { + // GTM 15.2: matrix with enlarged square brackets. + let expr = r#"[ + ab + cd + ]"#; + test_braille("UEB", expr, "⠸⠀⠨⠣⠁⠀⠃⠠⠨⠜⠸⠀⠨⠣⠰⠉⠀⠙⠠⠨⠜")?; + return Ok(()); +} + +#[test] +fn matrix_15_2_3() -> Result<()> { + // Single-row: no enlarged (dot-6) markers (UEB_Rules one-row path). + let expr = r#"( + 123 + )"#; + test_braille("UEB", expr, "⠐⠣⠼⠁⠀⠼⠃⠀⠼⠉⠐⠜")?; + return Ok(()); +} + +#[test] +fn determinant_15_3_1() -> Result<()> { + // GTM 15.3: determinant with enlarged vertical bars. + let expr = r#"| + 12 + 34 + |"#; + test_braille("UEB", expr, "⠸⠀⠸⠳⠼⠁⠀⠼⠃⠠⠸⠳⠸⠀⠸⠳⠼⠉⠀⠼⠙⠠⠸⠳")?; + return Ok(()); +} + #[test] fn chem_16_2_8() -> Result<()> { let expr = "Ca(OH)2"; From 18859bca60d88bbac58dfe02de912e733e21aafe Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 20 Aug 2026 00:40:44 -0700 Subject: [PATCH 065/120] Bring over fixes to the minified yaml files. --- src/bin/package_rules.rs | 91 +++++++++++++++++++-- src/main.rs | 45 ++--------- src/prefs.rs | 7 +- src/rules_archive.rs | 167 ++++++++++++++++++++++++++++++++------- 4 files changed, 237 insertions(+), 73 deletions(-) diff --git a/src/bin/package_rules.rs b/src/bin/package_rules.rs index 261059bea..900af235f 100644 --- a/src/bin/package_rules.rs +++ b/src/bin/package_rules.rs @@ -15,14 +15,14 @@ use std::process::ExitCode; #[command( name = "package-rules", about = "Package MathCAT Rules/ into a zip archive. \ - Use --minimize to strip comments from Languages unicode YAML." + Use --minimize to rewrite all Rules YAML in compact flow style." )] struct Options { /// Path to the Rules directory source: PathBuf, /// Output zip file path (e.g. Rules.zip) output: PathBuf, - /// Emit compact unicode.yaml / unicode-full.yaml without comments (speech languages only) + /// Rewrite all Rules YAML as compact flow YAML without comments #[arg(long)] minimize: bool, } @@ -119,6 +119,23 @@ mod tests { assert_eq!(docs.len(), 1); } + #[test] + fn minimize_yaml_uses_flow_style() { + let source = "---\n# comment\n - \"+\": [t: \"plus\"] # inline\n - \"-\": [t: \"minus\"]\n"; + let minimized = minimize_yaml_text(source).unwrap(); + assert!( + minimized.contains('[') && minimized.contains('{'), + "expected flow collections, got: {minimized}" + ); + assert!( + !minimized.lines().any(|line| line.trim_start().starts_with("- ")), + "expected no block-sequence dashes, got: {minimized}" + ); + let original = yaml_rust::YamlLoader::load_from_str(source).unwrap(); + let roundtrip = yaml_rust::YamlLoader::load_from_str(&minimized).unwrap(); + assert_eq!(original, roundtrip); + } + #[test] fn minimize_quotes_infinity() { let source = "---\n - \"∞\": [t: \"infinity\"]\n"; @@ -132,6 +149,22 @@ mod tests { assert_eq!(docs.len(), 1); } + #[test] + fn minimize_roundtrips_en_unicode_files() { + for name in ["unicode.yaml", "unicode-full.yaml"] { + let path = std::path::Path::new("Rules/Languages/en").join(name); + let source = fs::read_to_string(&path).expect(name); + let minimized = minimize_yaml_text(&source).unwrap(); + assert!( + !minimized.lines().any(|line| line.trim_start().starts_with("- ")), + "{name} still uses block sequences" + ); + let original = yaml_rust::YamlLoader::load_from_str(&source).unwrap(); + let roundtrip = yaml_rust::YamlLoader::load_from_str(&minimized).unwrap(); + assert_eq!(original, roundtrip, "{name} did not round-trip"); + } + } + #[test] fn package_rules_standard_zip_layout() { let tmp = tempdir().unwrap(); @@ -161,8 +194,43 @@ mod tests { assert_eq!(unicode.replace("\r\n", "\n"), "- \"+\": [t: \"plus\"]\n"); } + fn walk_yaml_files(dir: &std::path::Path, out: &mut Vec) { + for entry in fs::read_dir(dir).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + if path.is_dir() { + walk_yaml_files(&path, out); + } else if is_yaml_file(&path) { + out.push(path); + } + } + } + #[test] - fn should_not_minimize_braille_unicode() { + fn minimize_roundtrips_all_rules_yaml() { + let mut paths = Vec::new(); + walk_yaml_files(std::path::Path::new("Rules"), &mut paths); + assert!(!paths.is_empty()); + for path in paths { + if path.components().any(|c| c.as_os_str() == SKIP_LANGUAGE_DIR) { + continue; + } + let source = fs::read_to_string(&path).unwrap_or_else(|e| panic!("{}: {e}", path.display())); + let minimized = minimize_yaml_text(&source) + .unwrap_or_else(|e| panic!("{}: {e}", path.display())); + assert!( + !minimized.lines().any(|line| line.trim_start().starts_with("- ")), + "{} still uses block sequences", + path.display() + ); + let original = yaml_rust::YamlLoader::load_from_str(&source).unwrap(); + let roundtrip = yaml_rust::YamlLoader::load_from_str(&minimized).unwrap(); + assert_eq!(original, roundtrip, "{} did not round-trip", path.display()); + } + } + + #[test] + fn minimize_braille_unicode() { let tmp = tempdir().unwrap(); let rules = tmp.path().join("Rules"); let braille = rules.join("Braille").join("Nemeth"); @@ -172,11 +240,16 @@ mod tests { let output = tmp.path().join("Rules-minimized.zip"); let minimized = package_rules(&rules, &output, true, downloadable_compression()).unwrap(); - assert_eq!(minimized, 0); + assert_eq!(minimized, 1); let bytes = fs::read(&output).unwrap(); let text = read_nested_zip_text(&bytes, "Rules/Braille/Nemeth/Nemeth.zip", "unicode.yaml"); - assert_eq!(text.replace("\r\n", "\n"), source.replace("\r\n", "\n")); + assert!(!text.contains('#')); + assert!(text.contains('[') && text.contains('{')); + assert!(!text.lines().any(|line| line.trim_start().starts_with("- "))); + let original = yaml_rust::YamlLoader::load_from_str(source).unwrap(); + let roundtrip = yaml_rust::YamlLoader::load_from_str(&text).unwrap(); + assert_eq!(original, roundtrip); } #[test] @@ -198,7 +271,7 @@ mod tests { let output = tmp.path().join("Rules-minimized.zip"); let minimized = package_rules(&rules, &output, true, downloadable_compression()).unwrap(); - assert_eq!(minimized, 1); + assert_eq!(minimized, 2); let bytes = fs::read(&output).unwrap(); let names = outer_names(&bytes); @@ -208,8 +281,14 @@ mod tests { let unicode = read_nested_zip_text(&bytes, "Rules/Languages/en/en.zip", "unicode.yaml"); let defs = read_nested_zip_text(&bytes, "Rules/Languages/en/en.zip", "definitions.yaml"); assert!(!unicode.contains('#')); + assert!(unicode.contains('[') && unicode.contains('{')); + assert!(!unicode.lines().any(|line| line.trim_start().starts_with("- "))); + assert!(!defs.contains('#')); + assert!(defs.contains('[') && defs.contains('{')); + assert!(!defs.lines().any(|line| line.trim_start().starts_with("- "))); assert!(defs.contains("real=inf")); assert_eq!(yaml_rust::YamlLoader::load_from_str(&unicode).unwrap().len(), 1); + assert_eq!(yaml_rust::YamlLoader::load_from_str(&defs).unwrap().len(), 1); } #[test] diff --git a/src/main.rs b/src/main.rs index 506bd15d7..4a5a8c6fb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,39 +36,8 @@ fn main() { .init(); let expr = r#" - - - - e - - − - - 1 - 2 - - - - - ( - - - - x - − - μ - - σ - - - ) - - - 2 - - - - - + RT⌢ + "#; // let expr = " // @@ -308,7 +277,7 @@ fn main() { // info!("#xpath = {}; duplicates = {}", xpath_counts.0, xpath_counts.1); // info!("Time taken (second time for speech + braille): {}ms", instant.elapsed().as_millis()); // debug!("Hashmap sizes:\n{}", libmathcat::speech::SpeechRules::print_sizes()); - timing_test(expr, 5000); + timing_test(expr, 100); } @@ -327,10 +296,10 @@ fn timing_test(expr: &str, n_loops: usize) { Ok(_) =>( ), Err(e) => {eprintln!("{}", errors_to_string(&e)); exit(1);}, } - match get_braille("") { - Ok(_) => (), - Err(e) => {eprintln!("{}", errors_to_string(&e)); exit(1);}, - } + // match get_braille("") { + // Ok(_) => (), + // Err(e) => {eprintln!("{}", errors_to_string(&e)); exit(1);}, + // } } info!("Time taken (time for set, speech, {} braille averaged over {} loops): {}ms", get_preference("BrailleCode").unwrap(), n_loops, instant.elapsed().as_millis() as f64/n_loops_float); diff --git a/src/prefs.rs b/src/prefs.rs index 2fe69cdf3..4dc51c4bc 100644 --- a/src/prefs.rs +++ b/src/prefs.rs @@ -1288,8 +1288,11 @@ cfg_if::cfg_if! {if #[cfg(not(feature = "include-zip"))] { let braille_before = interface::get_braille("").unwrap(); let unicode_file = temp_rules_dir.join("Braille/Nemeth/unicode.yaml"); let unicode_contents = fs::read_to_string(&unicode_file).unwrap(); - // remap the digit '1' to the (distinctive) full braille cell '⠿' - let changed_unicode = unicode_contents.replace("\"1\": [t: \"N⠂\"]", "\"1\": [t: \"N⠿\"]"); + // Remap digit '1' to the distinctive full braille cell '⠿'. + // Source Rules use block style (`"1": [t: "N⠂"]`); Rules-minimized.zip uses flow (`{"1": [{t: N⠂}]}`). + let changed_unicode = unicode_contents + .replace("\"1\": [t: \"N⠂\"]", "\"1\": [t: \"N⠿\"]") + .replace("{\"1\": [{t: N⠂}]}", "{\"1\": [{t: N⠿}]}"); assert_ne!(changed_unicode, unicode_contents, "expected to find the Nemeth mapping for '1'"); fs::write(&unicode_file, changed_unicode).unwrap(); sleep(Duration::from_millis(5)); diff --git a/src/rules_archive.rs b/src/rules_archive.rs index 108220c28..70d8d81fe 100644 --- a/src/rules_archive.rs +++ b/src/rules_archive.rs @@ -13,12 +13,11 @@ use std::fs::{self, File, read_dir}; use std::io::{self, Cursor, Read, Seek, Write}; use std::path::{Path, PathBuf}; -use yaml_rust::{YamlEmitter, YamlLoader}; +use yaml_rust::{Yaml, YamlLoader}; use zip::write::{SimpleFileOptions, ZipWriter}; use zip::CompressionMethod; const YAML_SUFFIXES: [&str; 2] = ["yaml", "yml"]; -const MINIMIZE_NAMES: [&str; 2] = ["unicode.yaml", "unicode-full.yaml"]; const SKIP_LANGUAGE_DIR: &str = "zz"; const ARCHIVE_ROOT: &str = "Rules"; @@ -83,17 +82,7 @@ fn file_name_str(path: &Path) -> io::Result { .ok_or_else(|| io::Error::other(format!("non-UTF-8 file name: {}", path.display())))?; return Ok(name.to_string()); } - -/// Speech unicode YAML only — braille `unicode.yaml` stays verbatim. -fn should_minimize(path: &Path) -> bool { - let name = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); - if !MINIMIZE_NAMES.contains(&name) { - return false; - } - return path.components().any(|c| c.as_os_str() == "Languages"); -} - -/// Load YAML, drop comments, re-emit. Returns `Err` if yaml-rust cannot parse the file. +/// Load YAML, drop comments, re-emit in flow style. Returns `Err` if yaml-rust cannot parse the file. pub fn minimize_yaml_text(text: &str) -> Result { let normalized = text.replace('\t', " "); let docs = YamlLoader::load_from_str(&normalized).map_err(|e| e.to_string())?; @@ -101,16 +90,13 @@ pub fn minimize_yaml_text(text: &str) -> Result { return Ok(String::new()); } let mut out = String::new(); - { - let mut emitter = YamlEmitter::new(&mut out); - emitter.compact(true); - for doc in &docs { - emitter.dump(doc).map_err(|e| e.to_string())?; + for (i, doc) in docs.iter().enumerate() { + if i > 0 { + out.push_str("\n---\n"); } + emit_flow(&mut out, doc)?; } - if !out.ends_with('\n') { - out.push('\n'); - } + out.push('\n'); // yaml-rust quotes scalars that parse as f64 (including "infinity"); keep a guard in case // a future emitter change stops doing that — unquoted `infinity` becomes a float on load. if looks_like_unquoted_infinity(&out) { @@ -119,15 +105,143 @@ pub fn minimize_yaml_text(text: &str) -> Result { return Ok(out); } +fn emit_flow(out: &mut String, yaml: &Yaml) -> Result<(), String> { + match yaml { + Yaml::Array(items) => { + out.push('['); + for (i, item) in items.iter().enumerate() { + if i > 0 { + out.push(','); + } + emit_flow(out, item)?; + } + out.push(']'); + } + Yaml::Hash(hash) => { + out.push('{'); + for (i, (key, value)) in hash.iter().enumerate() { + if i > 0 { + out.push(','); + } + emit_flow(out, key)?; + out.push_str(": "); + emit_flow(out, value)?; + } + out.push('}'); + } + Yaml::String(s) => emit_yaml_string(out, s), + Yaml::Boolean(true) => out.push_str("true"), + Yaml::Boolean(false) => out.push_str("false"), + Yaml::Integer(i) => out.push_str(&i.to_string()), + Yaml::Real(s) => out.push_str(s), + Yaml::Null | Yaml::BadValue => out.push('~'), + Yaml::Alias(_) => return Err("cannot minimize YAML that contains aliases".to_string()), + } + return Ok(()); +} + +fn emit_yaml_string(out: &mut String, s: &str) { + if need_quotes(s) { + escape_yaml_string(out, s); + } else { + out.push_str(s); + } +} + +/// Same quoting rules as yaml-rust 0.4's emitter, so load/emit round-trips. +fn need_quotes(string: &str) -> bool { + string.is_empty() + || string.starts_with(' ') + || string.ends_with(' ') + || string.starts_with(|character: char| { + matches!(character, '&' | '*' | '?' | '|' | '-' | '<' | '>' | '=' | '!' | '%' | '@') + }) + || string.contains(|character: char| { + matches!( + character, + ':' | '{' | '}' | '[' | ']' | ',' | '#' | '`' | '"' | '\'' | '\\' + | '\0'..='\x06' + | '\t' + | '\n' + | '\r' + | '\x0e'..='\x1a' + | '\x1c'..='\x1f' + ) + }) + || [ + "yes", "Yes", "YES", "no", "No", "NO", "True", "TRUE", "true", "False", "FALSE", + "false", "on", "On", "ON", "off", "Off", "OFF", "null", "Null", "NULL", "~", + ] + .contains(&string) + || string.starts_with('.') + || string.starts_with("0x") + || string.parse::().is_ok() + || string.parse::().is_ok() +} + +fn escape_yaml_string(out: &mut String, v: &str) { + out.push('"'); + let mut start = 0; + for (i, byte) in v.bytes().enumerate() { + let escaped = match byte { + b'"' => "\\\"", + b'\\' => "\\\\", + b'\x00' => "\\u0000", + b'\x01' => "\\u0001", + b'\x02' => "\\u0002", + b'\x03' => "\\u0003", + b'\x04' => "\\u0004", + b'\x05' => "\\u0005", + b'\x06' => "\\u0006", + b'\x07' => "\\u0007", + b'\x08' => "\\b", + b'\t' => "\\t", + b'\n' => "\\n", + b'\x0b' => "\\u000b", + b'\x0c' => "\\f", + b'\r' => "\\r", + b'\x0e' => "\\u000e", + b'\x0f' => "\\u000f", + b'\x10' => "\\u0010", + b'\x11' => "\\u0011", + b'\x12' => "\\u0012", + b'\x13' => "\\u0013", + b'\x14' => "\\u0014", + b'\x15' => "\\u0015", + b'\x16' => "\\u0016", + b'\x17' => "\\u0017", + b'\x18' => "\\u0018", + b'\x19' => "\\u0019", + b'\x1a' => "\\u001a", + b'\x1b' => "\\u001b", + b'\x1c' => "\\u001c", + b'\x1d' => "\\u001d", + b'\x1e' => "\\u001e", + b'\x1f' => "\\u001f", + b'\x7f' => "\\u007f", + _ => continue, + }; + if start < i { + out.push_str(&v[start..i]); + } + out.push_str(escaped); + start = i + 1; + } + if start != v.len() { + out.push_str(&v[start..]); + } + out.push('"'); +} + fn looks_like_unquoted_infinity(yaml: &str) -> bool { - // Match a YAML value `infinity` that is not inside quotes. + // Block-style leftovers and flow mappings (`: infinity}` / `: infinity,`). for line in yaml.lines() { let trimmed = line.trim(); if trimmed == "infinity" || trimmed.ends_with(": infinity") || trimmed.ends_with("- infinity") { return true; } } - return false; + return yaml.contains(": infinity,") || yaml.contains(": infinity}") || yaml.contains(": infinity]"); } fn minimize_yaml_file(src: &Path, dst: &Path) -> Result<(), String> { @@ -143,9 +257,8 @@ fn minimize_yaml_file(src: &Path, dst: &Path) -> Result<(), String> { return Ok(()); } -/// Copy a Rules tree. When `minify` is true, rewrite `Languages/**/unicode.yaml` and -/// `unicode-full.yaml` (comments stripped). `Languages/zz` is omitted. Returns the number of -/// files successfully minimized. +/// Copy a Rules tree. When `minify` is true, rewrite every YAML file as comment-free flow YAML. +/// `Languages/zz` is omitted. Returns the number of files successfully minimized. pub fn copy_rules_tree(src: &Path, dst: &Path, minify: bool) -> io::Result { fs::create_dir_all(dst)?; return copy_dir(src, dst, minify, false); @@ -167,7 +280,7 @@ fn copy_dir(src: &Path, dst: &Path, minify: bool, parent_is_languages: bool) -> if src_path.is_dir() { fs::create_dir_all(&dst_path)?; minimized += copy_dir(&src_path, &dst_path, minify, name == "Languages")?; - } else if minify && should_minimize(&src_path) { + } else if minify && is_yaml_file(&src_path) { match minimize_yaml_file(&src_path, &dst_path) { Ok(()) => minimized += 1, Err(e) => { From 4b9d19ce367e8d4af45b24fe0840f00f64b5133f Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 20 Aug 2026 00:41:25 -0700 Subject: [PATCH 066/120] bump release number --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 32e83cf27..768d8bd50 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mathcat" -version = "0.7.6-rc.1" +version = "0.7.6-rc.2" authors = ["Neil Soiffer "] license = "MIT" description = "MathCAT: Math Capable Assistive Technology ('Speech and braille from MathML')" From eaa36e9ed03bfb147e66eb275186b718af464abd Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Thu, 20 Aug 2026 00:44:35 -0700 Subject: [PATCH 067/120] Fix #698 -- merge arc-like symbols under restricted condidtions --- src/canonicalize.rs | 128 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) diff --git a/src/canonicalize.rs b/src/canonicalize.rs index 380c4de0c..8b91aff8e 100644 --- a/src/canonicalize.rs +++ b/src/canonicalize.rs @@ -776,7 +776,53 @@ impl CanonicalizeContext { fn contains_currency(s: &str) -> bool { s.chars().any(is_currency_symbol) - } + } + + /// Geometry arc names like AB / ABC: a leaf whose text is 2–3 ASCII capitals. + fn is_two_or_three_ascii_capitals(base: Element) -> bool { + if !is_leaf(base) { + return false; + } + let text = as_str!(as_text(base)); + let n = text.chars().count(); + return (n == 2 || n == 3) && text.chars().all(|c| c.is_ascii_uppercase()); + } + + /// `mrow` of 2 or 3 capital ``s, optionally separated by U+2062 / U+2063 ``s. + fn is_mrow_of_two_or_three_cap_identifiers(base: Element) -> bool { + if name(base) != "mrow" { + return false; + } + let children = base.children(); + if children.is_empty() { + return false; + } + let mut n_mi = 0; + let mut prev_was_mo = false; + for (i, child) in children.iter().enumerate() { + let child = as_element(*child); + if name(child) == "mi" { + let text = as_str!(as_text(child)); + if !(text.chars().count() == 1 && text.chars().next().unwrap().is_ascii_uppercase()) { + return false; + } + n_mi += 1; + prev_was_mo = false; + } else if name(child) == "mo" { + if i == 0 || prev_was_mo { + return false; + } + let text = as_str!(as_text(child)); + if text != "\u{2062}" && text != "\u{2063}" { + return false; + } + prev_was_mo = true; + } else { + return false; + } + } + return !prev_was_mo && (n_mi == 2 || n_mi == 3); + } // begin by cleaning up empty elements // debug!("clean_mathml\n{}", mml_to_string(mathml)); @@ -985,10 +1031,22 @@ impl CanonicalizeContext { } }, "..." => { - if name(get_parent(mathml)) != "mover" { + if parent_name != "mover" { mathml.set_text("…"); } // name might need to change -- checked below }, + "⌢" | "⏜" => { // lookalikes: U+2322 (“Frown”), U+23DC (“Top Parenthesis”) + // arc should be over constructs like AB or ABC + if parent_name == "mover" { + let parent = get_parent(mathml); + let base = as_element(parent.children()[0]); // already made sure correct number of children + if base != mathml && + (is_two_or_three_ascii_capitals(base) || is_mrow_of_two_or_three_cap_identifiers(base)) { + mathml.set_text("⌒"); // U+2312 + } + } + return Some(mathml); + }, ":" => { if is_ratio(mathml) { mathml.set_text("∶"); // ratio U+2236 @@ -6022,6 +6080,72 @@ mod canonicalize_tests { are_strs_canonically_equal_result(test_str, target_str, &[]) } + #[test] + fn clean_up_arc_lookalike_mrow() -> Result<()> { + // Frown over two capital identifiers is rewritten to the arc operator (U+2312) + let test_str = "ABC⌢"; + let target_str = " + + + A + ⁣ + B + ⁣ + C + + ⌒ + + "; + are_strs_canonically_equal_result(test_str, target_str, &[]) + } + + #[test] + fn clean_up_arc_lookalike_mrow_invisible_sep() -> Result<()> { + // Same rewrite when the capital identifiers are already separated by U+2063 + let test_str = "B⁣C⌢"; + let target_str = " + + + B + ⁣ + C + + ⌒ + + "; + are_strs_canonically_equal_result(test_str, target_str, &[]) + } + + #[test] + fn clean_up_arc_lookalike_mtext() -> Result<()> { + // Frown over a 2-letter capital mtext is rewritten to the arc operator (U+2312) + let test_str = "BC⌢"; + let target_str = " + + + B + ⁣ + C + + ⌒ + + "; + are_strs_canonically_equal_result(test_str, target_str, &[]) + } + + #[test] + fn clean_up_arc_lookalike_not_letters() -> Result<()> { + // Frown over a single lowercase identifier is left as a frown, not an arc + let test_str = "z⌢"; + let target_str = " + + z + ⌢ + + "; + are_strs_canonically_equal_result(test_str, target_str, &[]) + } + #[test] fn roman_numeral() -> Result<()> { let test_str = "XLVIII +mmxxvi"; From cf0b751b40a03c3d64d851f5b933bcfa21688087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Arborealis=20L=C3=B6tberg?= <143188129+TimArborealis@users.noreply.github.com> Date: Thu, 20 Aug 2026 16:19:15 +0200 Subject: [PATCH 068/120] Update user guide Updated English user guide with info on how to use MathCAT as an addon in NVDA. --- docs/users.md | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/docs/users.md b/docs/users.md index a5ab487d3..6328e522f 100644 --- a/docs/users.md +++ b/docs/users.md @@ -10,6 +10,8 @@ MathCAT is a tool used together with a screen reader. Through MathCAT, you can h In screen readers NVDA and JAWS, MathCAT comes built-in. This means you don’t have to install it as an addon. +MathCAT is still available as an addon to NVDA. You can use it if you want access to the very latest features between NVDA releases. [How to use MathCAT as an addon is described here](#how-to-install-mathcat-as-an-addon-in-nvda). + ## Get started with MathCAT Use your screen reader on a web page or in an e-book as usual. When you get to a mathematical expression, the screen reader will read it out automatically. If you want to investigate the expression more closely you can activate the navigation mode by pressing Space. In NVDA, the NVDA-key+Alt+M can also be used. If you want to leave the navigation mode, press Escape. @@ -137,4 +139,42 @@ Note: tabular math means mathematical content in a table structure such as a mat ## Do you have feedback on MathCAT? -MathCAT is under active development, and we want your feedback. Do you have a feature request, or have you found any bugs? Please create an issue on [MathCAT's GitHub page](https://github.com/daisy/MathCAT/issues). You are also welcome to get in touch by signing up to [the mailing list of the DAISY MathCAT working group](https://www.surveymonkey.com/r/HXTLXGN). \ No newline at end of file +MathCAT is under active development, and we want your feedback. Do you have a feature request, or have you found any bugs? Please create an issue on [MathCAT's GitHub page](https://github.com/daisy/MathCAT/issues). You are also welcome to get in touch by signing up to [the mailing list of the DAISY MathCAT working group](https://www.surveymonkey.com/r/HXTLXGN). + +## How to install MathCAT as an addon in NVDA + +To use MathCAT as an add-on, do the following: + +1. Open the NVDA menu. +2. Choose Tools and then Add-on store. +3. In the add-on store, choose Available add-ons. + + The channel settings have the following options: + - Stable: Will only search for stable add-ons + - All: Will search for all add-ons + - Beta: Will search for add-ons that are in beta + - Dev: Will search for add-ons in development + + Choose option "All". + +4. Search for MathCAT and install. + +5. In the settings menu, you can control how add-ons are updated: + + - Open the NVDA menu + - Choose settings and Add-on Store + + For Automatic updates, you can choose between: + - Notify + - Update Automatically + - Deactivated + + For Default update channel, you have the following options: + + - Same: If automatic updates is set to “Update Automatically” and your add-on is a beta, it will update to the first available beta. + - Any: It will update to any version of the add-on. + - Do not update + - Stable: It will only update to stable versions. + - Beta or dev: It will update to beta or dev versions. + - Beta: It will update to beta versions. + - Dev: It will update to dev versions. \ No newline at end of file From c7c25faf8160add1d189dac4f2080a5d476006d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Arborealis=20L=C3=B6tberg?= <143188129+TimArborealis@users.noreply.github.com> Date: Fri, 21 Aug 2026 10:20:53 +0200 Subject: [PATCH 069/120] Updated acknowledgements Updated acknowledgements with French, Hungarian and Polish translators. --- docs/index.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/index.md b/docs/index.md index f0e944723..36b4ca41d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -171,13 +171,16 @@ Several people helped out in various ways with the project. I am very grateful f Translators: * Chinese (Traditional) -- Hon-Jang Yang -* Finnish -- Sami Määttä, Accessibility Library Celia, and Essi Viippola, freelancer +* Finnish -- Sami Määttä (Accessibility Library Celia), and Essi Viippola (freelancer) +* French -- Olivier Thiffault (Technologies Humanware) * German -- Nazli Andjic, Robert Graf and Paul Libbrecht (IU International University of Applied Sciences) +* Hungarian -- Attila Hammer (IT Foundation for the Visually Impaired) * Indonesian -- Dr. Pinta Deniyanti Sampoerno, M.Si; Dr. Meiliasari, S.Pd., M.Sc; and Ari Hendarno, S.Pd., M.Kom. -* Norwegian -- Marthe Gjelstad, National Library of Norway, Kvile +* Norwegian -- Marthe Gjelstad (National Library of Norway), Kvile +* Polish -- Michał Dziwisz (Fundacja Instytut Rozwoju Regionalnego) * Russian (both speech and braille) -- Danil Kostenkov * Spanish -- Noelia Ruiz Martínez (also helped with NVDA add-on development) and María Allo Roldán -* Swedish -- Tim Arborealis Lötberg, Swedish Agency for Accessible Media (MTM) and Anders Eklund, SPSM +* Swedish -- Tim Arborealis Lötberg (MTM), and Anders Eklund (SPSM) * Vietnamese -- Dang Hoai Phúc and Trang Pham * Others??? -- please volunteer so I can list you here... From 76b3e59ed385a667723f8e070e8e966845ba00cf Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Fri, 21 Aug 2026 18:15:57 -0700 Subject: [PATCH 070/120] fix #703 --- Rules/Braille/Nemeth/unicode-full.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rules/Braille/Nemeth/unicode-full.yaml b/Rules/Braille/Nemeth/unicode-full.yaml index 935933a2a..30ffc561d 100644 --- a/Rules/Braille/Nemeth/unicode-full.yaml +++ b/Rules/Braille/Nemeth/unicode-full.yaml @@ -801,7 +801,7 @@ - "⋁": [t: "⠈⠬"] # 0x22c1 - "⋂": [t: "⠨⠩"] # 0x22c2 - "⋃": [t: "⠨⠬"] # 0x22c3 - - "⋄": [t: "⠫⠩"] # 0x22c4 + - "⋄": [t: "⠫⠙"] # 0x22c4 - "⋜": [t: "⠱⠐⠅"] # 0x22dc - "⋝": [t: "⠱⠨⠂"] # 0x22dd - "⟂": [t: "⠫⠏"] # 0x27c2 From fc79ce983e4cfa34208d4e198ed1dc5fa9441a7e Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Fri, 21 Aug 2026 18:18:04 -0700 Subject: [PATCH 071/120] Fix up UEB mtable notations. There are made up rules (awaiting feedback from ICEB) about how to represent the start of a new row in linear form. add two dot-like characters used in an ellision example in a matrix --- Rules/Braille/UEB/UEB_Rules.yaml | 59 +++++++++------ Rules/Braille/UEB/unicode-full.yaml | 3 +- Rules/Braille/UEB/unicode.yaml | 1 + tests/braille/UEB/iceb.rs | 111 +++++++++++++++++++++++----- tests/braille/UEB/other.rs | 13 ++++ 5 files changed, 143 insertions(+), 44 deletions(-) diff --git a/Rules/Braille/UEB/UEB_Rules.yaml b/Rules/Braille/UEB/UEB_Rules.yaml index 04c63947d..4d5b8ca1d 100644 --- a/Rules/Braille/UEB/UEB_Rules.yaml +++ b/Rules/Braille/UEB/UEB_Rules.yaml @@ -138,42 +138,53 @@ # matrix and determinant are the same other than "matrix"/"determinant" based on the bracketing chars # we don't do spatial layout, instead the beginning/ending of each row uses the enlarged bracketing chars - - name: default-matrix - tag: mrow - variables: - - RowStart: "*[1]" - - RowEnd: "*[3]" - match: - - "*[2][self::m:mtable] and" - - (IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|')) - replace: [x: "*[2]"] + name: default-matrix + tag: mrow + variables: + - RowStart: "*[1]" + - RowEnd: "*[3]" + match: + - "*[2][self::m:mtable] and" + - (IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|')) + replace: [x: "*[2]"] + - - name: default-mtable - tag: mtable - match: "." - replace: [x: "*"] + name: default-mtable + tag: mtable + match: "." + variables: # should only be here for lines of equations -- made up dot-6, dot 0 as a parallel to dot six before parens + - RowStart: "IfThenElse($RowStart = '', '⠀', $RowStart)" + - RowEnd: "IfThenElse($RowEnd = '', '⠀', $RowEnd)" + replace: [x: "*"] + - name: default tag: [mtr, mlabeledtr] match: "." replace: - - test: - if: "../*[2]" # more than one row + - test: + if: "preceding-sibling::*" # between rows (not before the first) then: [t: "⠸W"] - - x: $RowStart - - test: + - test: + if: "../../self::m:mrow" # matrix, etc., or non-standalone lines (probably doesn't happen) + then: + - t: "⠠" + - x: $RowStart + - test: if: .[self::m:mlabeledtr] then: - - t: "⠗⠕⠺W⠇⠁⠃⠑⠇⠸⠒" # "row label:" - - x: "*[1]/*" # contents of row label - - test: + - t: "⠗⠕⠺W⠇⠁⠃⠑⠇⠸⠒" # "row label:" + - x: "*[1]/*" # contents of row label + - test: if: .[self::m:mlabeledtr] then: [x: "*[position()>1]"] else: [x: "*"] - - test: - if: "../*[2]" # more than one row - then: [t: "⠠"] - - x: $RowEnd + - test: + if: "../../self::m:mrow" # matrix, etc., or non-standalone lines (probably doesn't happen) + then: + - t: "⠠" + - x: $RowEnd + - name: default tag: mtd diff --git a/Rules/Braille/UEB/unicode-full.yaml b/Rules/Braille/UEB/unicode-full.yaml index 1df920330..717fa5355 100644 --- a/Rules/Braille/UEB/unicode-full.yaml +++ b/Rules/Braille/UEB/unicode-full.yaml @@ -130,7 +130,8 @@ - "‒": [t: "⠤"] # 0x2012 (figure dash) - "—": [t: "P⠠⠤"] # 0x2014 (em dash) - "•": [t: "⠸⠲"] # 0x2022 - - "․": [t: "⠲"] # 0x2024 + - "․": [t: "⠄"] # 0x2024 (one dot leader; GTM 15.4 matrix omission dot) + - "‥": [t: "⠲⠲"] # 0x2025 - "‼": [t: "⠖⠖"] # 0x203C - "‾": [t: "⠱"] # 0x203E diff --git a/Rules/Braille/UEB/unicode.yaml b/Rules/Braille/UEB/unicode.yaml index ccee08652..477946d70 100644 --- a/Rules/Braille/UEB/unicode.yaml +++ b/Rules/Braille/UEB/unicode.yaml @@ -118,6 +118,7 @@ - "†": [t: "⠈⠠⠹"] # 0x2020 (Dagger) - "‡": [t: "⠈⠠⠻"] # 0x2021 (Double dagger) - "…": [t: "c⠲c⠲c⠲"] # 0x2026 (Ellipsis) + - "⋯": [t: "⠄⠄⠄"] # 0x22EF (midline horizontal ellipsis; GTM 15.4 matrix omission) - "′": [t: "1⠶"] # 0x2032 (Prime sign) - "″": [t: "1⠶⠶"] # 0x2033 (Double prime sign) - "‴": [t: "1⠶⠶⠶"] # 0x2034 (Triple prime sign) diff --git a/tests/braille/UEB/iceb.rs b/tests/braille/UEB/iceb.rs index 2c7c9a5c7..b7f3a6560 100644 --- a/tests/braille/UEB/iceb.rs +++ b/tests/braille/UEB/iceb.rs @@ -1468,43 +1468,116 @@ fn binomial_14_3_3_2_mtable() -> Result<()> { #[test] fn matrix_15_2_1() -> Result<()> { // GTM 15.2 first example: I = [[1,0],[0,1]] with enlarged parentheses (one-line encoding). - let expr = r#"( - 10 - 01 - )"#; - test_braille("UEB", expr, "⠸⠀⠐⠣⠼⠁⠀⠼⠚⠠⠐⠜⠸⠀⠐⠣⠼⠚⠀⠼⠁⠠⠐⠜")?; + let expr = r#"I= + ( + 10 + 01 + ) + "#; + test_braille("UEB", expr, "⠠⠊⠀⠐⠶⠀⠠⠐⠣⠼⠁⠀⠼⠚⠠⠐⠜⠸⠀⠠⠐⠣⠼⠚⠀⠼⠁⠠⠐⠜")?; return Ok(()); } #[test] fn matrix_15_2_2() -> Result<()> { - // GTM 15.2: matrix with enlarged square brackets. - let expr = r#"[ - ab - cd - ]"#; - test_braille("UEB", expr, "⠸⠀⠨⠣⠁⠀⠃⠠⠨⠜⠸⠀⠨⠣⠰⠉⠀⠙⠠⠨⠜")?; + // GTM 15.2 second example: matrix multiplication (linearized with `⠸⠀` between rows). + // [1 2 3; 4 5 6] [1 2; -3 4; 5 -6] + let expr = r#" + [ + 123 + 456 + ] + [ + 12 + -34 + 5-6 + ] + "#; + // example has G1 passage mode start/end, but they are not needed and not included. + test_braille( + "UEB", + expr, + "⠠⠨⠣⠼⠁⠀⠼⠃⠀⠼⠉⠠⠨⠜⠸⠀⠠⠨⠣⠼⠙⠀⠼⠑⠀⠼⠋⠠⠨⠜⠠⠨⠣⠼⠁⠀⠼⠃⠠⠨⠜⠸⠀⠠⠨⠣⠐⠤⠼⠉⠀⠼⠙⠠⠨⠜⠸⠀⠠⠨⠣⠼⠑⠀⠐⠤⠼⠋⠠⠨⠜", + )?; return Ok(()); } #[test] fn matrix_15_2_3() -> Result<()> { - // Single-row: no enlarged (dot-6) markers (UEB_Rules one-row path). + // GTM 15.2 third example: (a −b ; −c d), linearized with `⠸⠀` between rows. + // Spec spatial braille (ASCII): ,"< a "-b,"> / ,"<"-c d,"> + // Spec spatial (Unicode): ⠠⠐⠣⠀⠁⠀⠐⠤⠃⠠⠐⠜ / ⠠⠐⠣⠐⠤⠉⠀⠙⠠⠐⠜ + // (extra space after the first-row open paren aligns columns so the minus stands out) let expr = r#"( - 123 + a-b + -cd )"#; - test_braille("UEB", expr, "⠐⠣⠼⠁⠀⠼⠃⠀⠼⠉⠐⠜")?; + test_braille("UEB", expr, "⠠⠐⠣⠁⠀⠐⠤⠃⠠⠐⠜⠸⠀⠠⠐⠣⠐⠤⠉⠀⠙⠠⠐⠜")?; return Ok(()); } #[test] fn determinant_15_3_1() -> Result<()> { - // GTM 15.3: determinant with enlarged vertical bars. + // GTM 15.3: |P| = |a b; c d| = ad − bc (determinant linearized with `⠸⠀` between rows). + let expr = r#" + |P| + = + | + ab + cd + | + = + ad-bc + "#; + test_braille( + "UEB", + expr, + "⠸⠳⠠⠏⠸⠳⠀⠐⠶⠀⠠⠸⠳⠁⠀⠃⠠⠸⠳⠸⠀⠠⠸⠳⠉⠀⠙⠠⠸⠳⠀⠐⠶⠀⠁⠙⠐⠤⠃⠉", + )?; + return Ok(()); +} + +#[test] +fn omission_15_4_1() -> Result<()> { + // GTM 15.4: determinant with omission dots (linearized with `⠸⠀` between rows). + // Spec spatial (ASCII): + // ;;;,_|a5#aa a5#ab 444 a5<#an>,_| + // ,_|a5#ba a5#bb 444 a5<#bn>,_| + // ,_| 4 4 444 4 ,_| + // ,_|a5 a5 444 a5 ,_|;' + // Spec uses . (⠄) / ⋯ (⠄⠄⠄) for omission dots; U+2024 / U+22EF in the MathML. let expr = r#"| - 12 - 34 - |"#; - test_braille("UEB", expr, "⠸⠀⠸⠳⠼⠁⠀⠼⠃⠠⠸⠳⠸⠀⠸⠳⠼⠉⠀⠼⠙⠠⠸⠳")?; + + a11 + a12 + ⋯ + + a1n + + + a21 + a22 + ⋯ + a2n + + + . + . + ⋯ + . + + + am1 + am2 + ⋯ + amn + + |"#; + test_braille( + "UEB", + expr, + "⠰⠰⠰⠠⠸⠳⠁⠢⠼⠁⠁⠀⠁⠢⠼⠁⠃⠀⠄⠄⠄⠀⠁⠢⠣⠼⠁⠝⠜⠠⠸⠳⠸⠀⠠⠸⠳⠁⠢⠼⠃⠁⠀⠁⠢⠼⠃⠃⠀⠄⠄⠄⠀⠁⠢⠣⠼⠃⠝⠜⠠⠸⠳⠸⠀⠠⠸⠳⠲⠀⠲⠀⠄⠄⠄⠀⠲⠠⠸⠳⠸⠀⠠⠸⠳⠁⠢⠣⠍⠼⠁⠜⠀⠁⠢⠣⠍⠼⠃⠜⠀⠄⠄⠄⠀⠁⠢⠣⠍⠝⠜⠠⠸⠳⠰⠄", + )?; return Ok(()); } diff --git a/tests/braille/UEB/other.rs b/tests/braille/UEB/other.rs index b31f9958f..f1beb55cc 100644 --- a/tests/braille/UEB/other.rs +++ b/tests/braille/UEB/other.rs @@ -255,3 +255,16 @@ fn ueb_italic_typeform_for_digits() -> Result<()> { return Ok(()); } +#[test] +fn multiple_lines() -> Result<()> { + init_logger(); + // Bare equation-line mtable: `⠸⠀` between rows, no start/end bounds. + let expr = "x=f(t)y=g(t)"; + test_braille( + "UEB", + expr, + "⠰⠭⠀⠐⠶⠀⠋⠐⠣⠞⠐⠜⠸⠀⠰⠽⠀⠐⠶⠀⠛⠐⠣⠞⠐⠜", + )?; + Ok(()) +} + From 1b32804d01fbe1b3a86fd5f023ef03cc5a969155 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Fri, 21 Aug 2026 20:04:30 -0700 Subject: [PATCH 072/120] accidentally picked up wrong version # when merged in release-candidate --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 768d8bd50..67522e0b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mathcat" -version = "0.7.6-rc.2" +version = "0.7.7-alpha.1" authors = ["Neil Soiffer "] license = "MIT" description = "MathCAT: Math Capable Assistive Technology ('Speech and braille from MathML')" From 7ee9e3a3fc688660eb012adf41d8ca308ba99123 Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Fri, 21 Aug 2026 20:05:16 -0700 Subject: [PATCH 073/120] bump version # --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 768d8bd50..26749dc34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mathcat" -version = "0.7.6-rc.2" +version = "0.7.6-rc.3" authors = ["Neil Soiffer "] license = "MIT" description = "MathCAT: Math Capable Assistive Technology ('Speech and braille from MathML')" From f89080fa73bb8d2441ae28bb91bfef2f020864ed Mon Sep 17 00:00:00 2001 From: mgros Date: Sun, 23 Aug 2026 00:59:29 +0200 Subject: [PATCH 074/120] add Math4VIP to German translators --- docs/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index 36b4ca41d..62805d15a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -166,14 +166,14 @@ Several people helped out in various ways with the project. I am very grateful f * Elaine A. Moore -- helped me to figure out what should and should not be said for chemistry, along with what makes sense as chemistry and what doesn't. * Richard Orme -- did all the work for the MathCAT NVDA settings dialog. * Sam Dooley, Murray Sargent, and Volker Sorge -- provided tables of Nemeth translations of characters and Nemeth tests -* Moritz Groß ([Math4VIP](https://www.math4vip.de/)) -- worked on various parts of the Rust code base, and built the Python tool for tracking localization progress. +* Moritz Groß ([Math4VIP](https://www.math4vip.de/), Marburg University) -- worked on various parts of the Rust code base, and built the Python tool for tracking localization progress. Translators: * Chinese (Traditional) -- Hon-Jang Yang * Finnish -- Sami Määttä (Accessibility Library Celia), and Essi Viippola (freelancer) * French -- Olivier Thiffault (Technologies Humanware) -* German -- Nazli Andjic, Robert Graf and Paul Libbrecht (IU International University of Applied Sciences) +* German -- Nazli Andjic, Robert Graf and Paul Libbrecht (IU International University of Applied Sciences), and Moritz Groß (Math4VIP, Marburg University) * Hungarian -- Attila Hammer (IT Foundation for the Visually Impaired) * Indonesian -- Dr. Pinta Deniyanti Sampoerno, M.Si; Dr. Meiliasari, S.Pd., M.Sc; and Ari Hendarno, S.Pd., M.Kom. * Norwegian -- Marthe Gjelstad (National Library of Norway), Kvile From 88129bed9597d4ed8ce92725b71f4bab49d497ee Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Sun, 23 Aug 2026 00:25:10 -0700 Subject: [PATCH 075/120] Fix up arc look-alike chars due to canonicalize change. Remove duplicate char definitions in unicode files. --- Rules/Braille/French/French_Rules.yaml | 2 +- Rules/Braille/French/unicode-full.yaml | 2 +- Rules/Braille/French/unicode.yaml | 12 +----------- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/Rules/Braille/French/French_Rules.yaml b/Rules/Braille/French/French_Rules.yaml index 3c77813a5..46c74a15c 100644 --- a/Rules/Braille/French/French_Rules.yaml +++ b/Rules/Braille/French/French_Rules.yaml @@ -424,7 +424,7 @@ - name: single-char-exceptions tag: [mover] - match: "*[2][translate(., \"⏜^→⇀¯⌣◌̌~\",'')='']" + match: "*[2][translate(., \"⌒⌢⏜^→⇀¯⌣◌̌~\",'')='']" replace: - x: "*[2]" - x: "*[1]" diff --git a/Rules/Braille/French/unicode-full.yaml b/Rules/Braille/French/unicode-full.yaml index c0bc5275e..65530bdf8 100644 --- a/Rules/Braille/French/unicode-full.yaml +++ b/Rules/Braille/French/unicode-full.yaml @@ -19,7 +19,7 @@ - "": [t: ""] # 0xfeff (BOM / zero-width no-break space) - - "⇌⥂⥄⥃": [t: "⠸⠻"] # 0x21CC/2942/2944/2943 equilibrium (incl. trend variants) + - "⥂⥄⥃": [t: "⠸⠻"] # 0x2942/2944/2943 equilibrium trend variants (⇌/0x21CC is in unicode.yaml) # Mappings for some new Unicode chemistry arrows/symbols # These repeat the short arrow version because that is what is in the spec (GTM 16), but the spec should really use the long version diff --git a/Rules/Braille/French/unicode.yaml b/Rules/Braille/French/unicode.yaml index 89f876bd6..694f6d73e 100644 --- a/Rules/Braille/French/unicode.yaml +++ b/Rules/Braille/French/unicode.yaml @@ -370,20 +370,10 @@ - "⊗": [t: "⠨⠔"] # 0x2297 (circle with interior cross (tensor product)) - "⊥": [t: "⠘⠳"] # 0x22A5 (is perpendicular to) - "⋅": [t: "⠔⠔"] # 0x22C5 (dot,and times) - - "⟵": [t: "⠸⠪"] # 0x27F5 (Long leftwards arrow ) - - "⟶": # 0x27F6 (Long rightwards arrow) - - test: - if: "parent::m:mover and not(following-sibling::*)" - then: [t: "⠨⠒"] - else: [t: "⠸⠱"] - - "⟷": [t: "⠸⠻"] # 0x27F7 (Long left right arrow) - - "⟸": [t: "⠐⠒"] # 0x27F8 (Long leftwards arrow ) - - "⟹": [t: "⠒⠂"] # 0x27F9 (Long rightwards arrow) - - "⟺": [t: "⠐⠒⠂"] # 0x27FA (Long left right arrow) - "⌄": [t: "⠸⠘⠒"] # 0x2304 (inverted caret) - "⌣": [t: "⠈⠈⠈"] # 0x2323 (smile) - - "⏜": [t: "⠈⠒"] # 0x23DC (arc concave upward) + - "⌒⌢⏜": [t: "⠈⠒"] # 0x2312 (ARC), 0x2322 (FROWN), 0x23DC (TOP PARENTHESIS) - "△": [t: "⠨⠘⠙"] # 0x25B3 (increment (laplacian Delta) ) - "✕": [t: "⠐⠐⠔"] # 0x2715 (multiplication X) - "⟧": [t: "⠨⠾"] # 0x27E7 (right barred bracket) From 9f8d81f29fa0bf983b7746d3168f74838f81ffea Mon Sep 17 00:00:00 2001 From: nsoiffer Date: Tue, 25 Aug 2026 01:47:42 -0700 Subject: [PATCH 076/120] fixes no-unsafe branch clippy issues --- src/braille.rs | 12 ++++++------ src/chemistry.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/braille.rs b/src/braille.rs index 553059bfc..1966f09c0 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -2430,10 +2430,10 @@ fn remove_optional_group_indicators(braille: &str) -> String { for (i, &ch) in chars.iter().enumerate() { if ch == 'b' { stack.push(i); - } else if ch == 'e' { - if let Some(b_idx) = stack.pop() { - pairs.push((b_idx, i)); - } + } else if ch == 'e' + && let Some(b_idx) = stack.pop() + { + pairs.push((b_idx, i)); } } @@ -2583,7 +2583,7 @@ fn french_cleanup(_pref_manager: Ref, raw_braille: String) -> // This reuses the code just for getting rid of unnecessary "L"s and "N"s let result = remove_unneeded_mode_changes(&result, UEB_Mode::Grade1, UEB_Duration::Passage); - debug!(" after removing mode changes={}", &result); + debug!(" after removing mode changes={}", result); // remove any grouping pair at the start or end -- we ensure they are a pair but making sure there is no "o" inside the string let result = if result.starts_with('o') && result.ends_with('c') && !result[1..result.len() - 1].contains('o') { @@ -3553,7 +3553,7 @@ impl NeedsToBeGrouped { }; debug!("needs_grouping_for_french: mathml (sans '-') = {}", mml_to_string(mathml)); - let node_name = name(mathml); + let node_name = as_str!(name(mathml)); let children = mathml.children(); match node_name { "mn" | "mi" | "mtext" => { diff --git a/src/chemistry.rs b/src/chemistry.rs index 78bbd3746..64295196a 100644 --- a/src/chemistry.rs +++ b/src/chemistry.rs @@ -534,9 +534,9 @@ fn set_marked_chemistry_attr(mathml: Element, chem: &str) { // inherit from the base let base = as_element(mathml.children()[0]); if let Some(base_chem) = base.attribute(CHEM_EQUATION_OPERATOR) { - mathml.set_attribute_value(CHEM_EQUATION_OPERATOR, base_chem.value()); + mathml.set_attribute_value(CHEM_EQUATION_OPERATOR, as_str!(base_chem.value())); } else if let Some(base_chem) = base.attribute(CHEM_FORMULA_OPERATOR) { - mathml.set_attribute_value(CHEM_FORMULA_OPERATOR, base_chem.value()); + mathml.set_attribute_value(CHEM_FORMULA_OPERATOR, as_str!(base_chem.value())); } } _ => error!("Internal error: {} should not be marked as 'MAYBE_CHEMISTRY'", tag_name), From 4952dd78b8ffcf138635c6290b26c8e852965bbb Mon Sep 17 00:00:00 2001 From: Richard Orme Date: Fri, 28 Aug 2026 00:58:45 +0100 Subject: [PATCH 077/120] Update MathCAT logo and add descriptive alt text. (#714) Co-authored-by: Cursor --- README.md | 2 +- docs/callers.md | 2 +- docs/helpers_old.md | 2 +- docs/index.md | 2 +- docs/ru/callers.md | 2 +- docs/ru/helpers.md | 2 +- docs/ru/index.md | 2 +- docs/ru/users.md | 2 +- docs/users_old.md | 2 +- logo.png | Bin 5162 -> 64842 bytes mathcat_logo_regular.png | Bin 0 -> 55590 bytes 11 files changed, 9 insertions(+), 9 deletions(-) create mode 100644 mathcat_logo_regular.png diff --git a/README.md b/README.md index de080bb0f..d34a66632 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MathCAT: Math Capable Assistive Technology -The MathCAT logo with a cat sitting on the text MathCAT +Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. is a library that supports conversion of MathML to: diff --git a/docs/callers.md b/docs/callers.md index 763a8484e..0fbe0f189 100644 --- a/docs/callers.md +++ b/docs/callers.md @@ -1,5 +1,5 @@ # MathCAT: Math Capable Assistive Technology - +Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. ## Information for AT and Other Library Users diff --git a/docs/helpers_old.md b/docs/helpers_old.md index 59b8387a3..dea983da6 100644 --- a/docs/helpers_old.md +++ b/docs/helpers_old.md @@ -1,4 +1,4 @@ -# Translator and Rule Developer Guide +# Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. Translator and Rule Developer Guide ## Information for MathCAT Rule Developers/Translators This page is a work-in-progress. diff --git a/docs/index.md b/docs/index.md index 62805d15a..e965c905d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,5 +1,5 @@ # MathCAT: Math Capable Assistive Technology - +Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. is a library that supports conversion of MathML to: * Speech strings with embedded speech engine commands diff --git a/docs/ru/callers.md b/docs/ru/callers.md index c189234e3..cc3117baa 100644 --- a/docs/ru/callers.md +++ b/docs/ru/callers.md @@ -5,7 +5,7 @@ ref: callers title: Интеграция MathCAT --- # MathCAT: средство обеспечения доступности математики - +Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. ## Информация для разработчиков ассистивных технологий и пользователей библиотеки diff --git a/docs/ru/helpers.md b/docs/ru/helpers.md index be36a6116..0cb0b8769 100644 --- a/docs/ru/helpers.md +++ b/docs/ru/helpers.md @@ -4,7 +4,7 @@ lang: ru ref: helpers title: Руководство переводчика и разработчика правил --- -# Руководство переводчика и разработчика правил +# Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. Руководство переводчика и разработчика правил ## Информация для разработчиков правил и переводчиков MathCAT Эта страница находится в процессе подготовки. diff --git a/docs/ru/index.md b/docs/ru/index.md index 25c7d1b5a..a577238ad 100644 --- a/docs/ru/index.md +++ b/docs/ru/index.md @@ -5,7 +5,7 @@ ref: index title: MathCAT --- # MathCAT: средство обеспечения доступности математики - +Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. — библиотека, которая преобразует MathML в: * текст для озвучивания со встроенными командами синтезатора речи; diff --git a/docs/ru/users.md b/docs/ru/users.md index 29d4fa9f1..bb4102cc5 100644 --- a/docs/ru/users.md +++ b/docs/ru/users.md @@ -4,7 +4,7 @@ lang: ru ref: users title: Руководство пользователя MathCAT --- -# Логотип MathCAT Руководство пользователя +# Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. Руководство пользователя ## Рекомендации по выбору голоса diff --git a/docs/users_old.md b/docs/users_old.md index 5acaecc8e..308f74bdb 100644 --- a/docs/users_old.md +++ b/docs/users_old.md @@ -3,7 +3,7 @@ layout: default lang: en ref: users --- -# MathCAT logo User Guide +# Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. User Guide ## Advice on Choosing a Voice diff --git a/logo.png b/logo.png index ca5b5c9f79a5ac789def198da43ada8e8810352f..51400749c09484d06e1157195d2a2cb12b116e3a 100644 GIT binary patch literal 64842 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 Date: Fri, 28 Aug 2026 19:56:34 +1000 Subject: [PATCH 078/120] fix clippy warning with new version of rust --- src/canonicalize.rs | 14 ++++++-------- src/chemistry.rs | 15 ++++++--------- src/tts.rs | 14 ++++++-------- 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/src/canonicalize.rs b/src/canonicalize.rs index 380c4de0c..d20374d49 100644 --- a/src/canonicalize.rs +++ b/src/canonicalize.rs @@ -2412,23 +2412,21 @@ impl CanonicalizeContext { // This is not yet in canonical form, so the fences may be siblings or siblings of the parent let preceding_siblings = as_element(children[0]).preceding_siblings(); let following_siblings = as_element(children[end-1]).following_siblings(); - let first_child; - let last_child; - if preceding_siblings.is_empty() && following_siblings.is_empty() { + + + let (first_child, last_child) = if preceding_siblings.is_empty() && following_siblings.is_empty() { // number spans all children, look to parent for fences let preceding_children = mrow.preceding_siblings(); let following_children = mrow.following_siblings(); if preceding_children.is_empty() || following_children.is_empty() { return true; // doesn't have left or right fence } - first_child = preceding_children[preceding_children.len()-1]; - last_child = following_children[0]; + (preceding_children[preceding_children.len()-1], following_children[0]) } else if preceding_siblings.is_empty() || following_siblings.is_empty() { return true; // can't be fences around it } else { - first_child = preceding_siblings[preceding_siblings.len()-1]; - last_child = following_siblings[0]; - } + (preceding_siblings[preceding_siblings.len()-1], following_siblings[0]) + }; let first_child = as_element(first_child); let last_child = as_element(last_child); return !(name(first_child) == "mo" && is_fence(first_child) && diff --git a/src/chemistry.rs b/src/chemistry.rs index 1176d8d3a..23de8668a 100644 --- a/src/chemistry.rs +++ b/src/chemistry.rs @@ -1384,17 +1384,14 @@ pub fn likely_adorned_chem_formula(mathml: Element) -> i32 { // prescripts are normally positive integers, chem 2.5.1 allows for a superscript for a Lewis dot // postscript should be a charge - let prescripts; - let postscripts; - if children.len() == 4 && name(as_element(children[1]))=="mprescripts" { // just prescripts - prescripts = &children[2..4]; - postscripts = &children[0..0]; // empty + + + let (prescripts, postscripts) = if children.len() == 4 && name(as_element(children[1]))=="mprescripts" { // just prescripts + (&children[2..4], &children[0..0]) // empty } else if children.len() == 6 && name(as_element(children[3]))=="mprescripts" { // pre and postscripts - prescripts = &children[4..6]; - postscripts = &children[1..3]; // empty + (&children[4..6], &children[1..3]) // empty } else if children.len() == 3 || children.len() == 5 { // just postscripts (simultaneous or offset) - prescripts = &children[0..0]; // empty - postscripts = &children[1..]; + (&children[0..0], &children[1..]) } else { return NOT_CHEMISTRY; }; diff --git a/src/tts.rs b/src/tts.rs index 91b05c0a8..c2f951b39 100644 --- a/src/tts.rs +++ b/src/tts.rs @@ -669,18 +669,16 @@ impl TTS { /// There is a bias towards pausing more _after_ longer strings. pub fn compute_auto_pause(&self, prefs: &PreferenceManager, before: &str, after: &str) -> Result { static REMOVE_XML: LazyLock = LazyLock::new(|| Regex::new(r"<.+?>").unwrap()); // punctuation ending with a '.' - let before_len; - let after_len; - match self { + + + let (before_len, after_len) = match self { TTS::SSML | TTS::SAPI5 => { - before_len = REMOVE_XML.replace_all(before, "").len(); - after_len = REMOVE_XML.replace_all(after, "").len(); + (REMOVE_XML.replace_all(before, "").len(), REMOVE_XML.replace_all(after, "").len()) }, _ => { - before_len = before.len(); - after_len = after.len(); + (before.len(), after.len()) }, - } + }; // pause values are not cut in stone // the calculation bias to 'previous' is based on MathPlayer which used '30 * #-of-descendants-on-left From 5a4aa2c35f38b1501900a61d1f4909664a2bc300 Mon Sep 17 00:00:00 2001 From: "U-rainshowers\\neils" Date: Fri, 28 Aug 2026 21:28:36 +1000 Subject: [PATCH 079/120] tried to align the MathCAT logo with the following text. --- README.md | 3 +-- docs/callers.md | 2 +- docs/helpers_old.md | 2 +- docs/index.md | 3 +-- docs/ru/callers.md | 2 +- docs/ru/helpers.md | 2 +- docs/ru/index.md | 3 +-- docs/ru/users.md | 2 +- docs/users_old.md | 2 +- 9 files changed, 9 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d34a66632..bf974dc3b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # MathCAT: Math Capable Assistive Technology -Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. -is a library that supports conversion of MathML to: +Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. is a library that supports conversion of MathML to: * Speech strings (in several languages) with embedded speech engine commands diff --git a/docs/callers.md b/docs/callers.md index 0fbe0f189..caff0437d 100644 --- a/docs/callers.md +++ b/docs/callers.md @@ -1,5 +1,5 @@ # MathCAT: Math Capable Assistive Technology -Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. +Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. ## Information for AT and Other Library Users diff --git a/docs/helpers_old.md b/docs/helpers_old.md index dea983da6..5266c8b3e 100644 --- a/docs/helpers_old.md +++ b/docs/helpers_old.md @@ -1,4 +1,4 @@ -# Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. Translator and Rule Developer Guide +# Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. Translator and Rule Developer Guide ## Information for MathCAT Rule Developers/Translators This page is a work-in-progress. diff --git a/docs/index.md b/docs/index.md index e965c905d..b4283d1d5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,5 @@ # MathCAT: Math Capable Assistive Technology -Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. -is a library that supports conversion of MathML to: +Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. is a library that supports conversion of MathML to: * Speech strings with embedded speech engine commands * Braille (Nemeth, UEB Technical, and eventually other braille math codes) diff --git a/docs/ru/callers.md b/docs/ru/callers.md index cc3117baa..e9a1270cf 100644 --- a/docs/ru/callers.md +++ b/docs/ru/callers.md @@ -5,7 +5,7 @@ ref: callers title: Интеграция MathCAT --- # MathCAT: средство обеспечения доступности математики -Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. +Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. ## Информация для разработчиков ассистивных технологий и пользователей библиотеки diff --git a/docs/ru/helpers.md b/docs/ru/helpers.md index 0cb0b8769..ebcd6e8c4 100644 --- a/docs/ru/helpers.md +++ b/docs/ru/helpers.md @@ -4,7 +4,7 @@ lang: ru ref: helpers title: Руководство переводчика и разработчика правил --- -# Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. Руководство переводчика и разработчика правил +# Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. Руководство переводчика и разработчика правил ## Информация для разработчиков правил и переводчиков MathCAT Эта страница находится в процессе подготовки. diff --git a/docs/ru/index.md b/docs/ru/index.md index a577238ad..1ad5429e1 100644 --- a/docs/ru/index.md +++ b/docs/ru/index.md @@ -5,8 +5,7 @@ ref: index title: MathCAT --- # MathCAT: средство обеспечения доступности математики -Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. -— библиотека, которая преобразует MathML в: +Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. — библиотека, которая преобразует MathML в: * текст для озвучивания со встроенными командами синтезатора речи; * Брайль (код Немета, технический вариант UEB, а в дальнейшем и другие математические системы записи Брайля); diff --git a/docs/ru/users.md b/docs/ru/users.md index bb4102cc5..f6d61a9e0 100644 --- a/docs/ru/users.md +++ b/docs/ru/users.md @@ -4,7 +4,7 @@ lang: ru ref: users title: Руководство пользователя MathCAT --- -# Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. Руководство пользователя +# Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. Руководство пользователя ## Рекомендации по выбору голоса diff --git a/docs/users_old.md b/docs/users_old.md index 308f74bdb..df32d6a45 100644 --- a/docs/users_old.md +++ b/docs/users_old.md @@ -3,7 +3,7 @@ layout: default lang: en ref: users --- -# Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. User Guide +# Logo for MathCAT. a brown cat sits upright. Its tail swoops down to form the capital letter C in the word MathCAT. User Guide ## Advice on Choosing a Voice From ed724d10b5c4ebe163caf9afef2f0f39630fee89 Mon Sep 17 00:00:00 2001 From: "U-rainshowers\\neils" Date: Fri, 28 Aug 2026 22:21:02 +1000 Subject: [PATCH 080/120] Add logo-inline.png for README and GitHub Pages docs --- docs/logo-inline.png | Bin 0 -> 42032 bytes logo-inline.png | Bin 0 -> 42032 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/logo-inline.png create mode 100644 logo-inline.png diff --git a/docs/logo-inline.png b/docs/logo-inline.png new file mode 100644 index 0000000000000000000000000000000000000000..cdca63cc38743d117de9fa51c4811ea6902337da GIT binary patch literal 42032 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 Date: Tue, 1 Sep 2026 11:46:45 +0200 Subject: [PATCH 081/120] Cred to Greek translators --- docs/index.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/index.md b/docs/index.md index b4283d1d5..9a34a7b71 100644 --- a/docs/index.md +++ b/docs/index.md @@ -173,13 +173,14 @@ Translators: * Finnish -- Sami Määttä (Accessibility Library Celia), and Essi Viippola (freelancer) * French -- Olivier Thiffault (Technologies Humanware) * German -- Nazli Andjic, Robert Graf and Paul Libbrecht (IU International University of Applied Sciences), and Moritz Groß (Math4VIP, Marburg University) +* Greek -- Theodora Antonakopoulou and Paraskevi Riga (National and Kapodistrian University of Athens) * Hungarian -- Attila Hammer (IT Foundation for the Visually Impaired) * Indonesian -- Dr. Pinta Deniyanti Sampoerno, M.Si; Dr. Meiliasari, S.Pd., M.Sc; and Ari Hendarno, S.Pd., M.Kom. -* Norwegian -- Marthe Gjelstad (National Library of Norway), Kvile +* Norwegian -- Marthe Gjelstad (National Library of Norway) and Kvile * Polish -- Michał Dziwisz (Fundacja Instytut Rozwoju Regionalnego) * Russian (both speech and braille) -- Danil Kostenkov * Spanish -- Noelia Ruiz Martínez (also helped with NVDA add-on development) and María Allo Roldán -* Swedish -- Tim Arborealis Lötberg (MTM), and Anders Eklund (SPSM) +* Swedish -- Tim Arborealis Lötberg (MTM) and Anders Eklund (SPSM) * Vietnamese -- Dang Hoai Phúc and Trang Pham * Others??? -- please volunteer so I can list you here... From 0932ffb8f4f0429518b28dd06071316d389a9b0b Mon Sep 17 00:00:00 2001 From: K4itsit Date: Wed, 2 Sep 2026 20:18:10 +0800 Subject: [PATCH 082/120] Fix outdated MathCAT project URLs (#732) Co-authored-by: k4its1t <306379390+k4its1t@users.noreply.github.com> --- Cargo.toml | 6 +++--- README.md | 2 +- Rules/Braille/ASCIIMath/ASCIIMath_Rules.yaml | 2 +- Rules/Braille/French/French_Rules.yaml | 2 +- Rules/Braille/LaTeX/LaTeX_Rules.yaml | 2 +- Rules/Braille/Nemeth/Nemeth_Rules.yaml | 4 ++-- Rules/Braille/Swedish/Swedish_Rules.yaml | 2 +- Rules/Braille/UEB/UEB_Rules.yaml | 2 +- Rules/Braille/Vietnam/Vietnam_Rules.yaml | 2 +- Rules/Intent/general.yaml | 2 +- docs/helpers_old.md | 2 +- docs/ru/helpers.md | 2 +- src/canonicalize.rs | 2 +- src/chemistry.rs | 2 +- src/navigate.rs | 2 +- src/speech.rs | 2 +- src/xpath_functions.rs | 2 +- tests/braille/Nemeth/other.rs | 14 +++++++------- tests/braille/Nemeth/rules.rs | 6 +++--- tests/braille/UEB/iceb.rs | 2 +- tests/braille/UEB/other.rs | 10 +++++----- 21 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 67522e0b3..fcf5428d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,9 +4,9 @@ version = "0.7.7-alpha.1" authors = ["Neil Soiffer "] license = "MIT" description = "MathCAT: Math Capable Assistive Technology ('Speech and braille from MathML')" -repository = "https://github.com/NSoiffer/MathCAT" -homepage = "https://nsoiffer.github.io/MathCAT/" -documentation = "https://nsoiffer.github.io/MathCAT/" +repository = "https://github.com/daisy/MathCAT" +homepage = "https://daisy.github.io/MathCAT/" +documentation = "https://daisy.github.io/MathCAT/" edition = "2024" exclude = ["src/main.rs", "docs", "BrailleDocs", "PythonScripts"] # should have "Rules/", but then one can't run build.rs to build the zip file diff --git a/README.md b/README.md index bf974dc3b..0ee92c6fa 100644 --- a/README.md +++ b/README.md @@ -16,4 +16,4 @@ There are four related projects that make use of MathCAT: MathCAT is used in many assistive technologies including NVDA and JAWS. -For more information, see the [full documentation](https://nsoiffer.github.io/MathCAT/). +For more information, see the [full documentation](https://daisy.github.io/MathCAT/). diff --git a/Rules/Braille/ASCIIMath/ASCIIMath_Rules.yaml b/Rules/Braille/ASCIIMath/ASCIIMath_Rules.yaml index d3cb9d090..edae6abab 100644 --- a/Rules/Braille/ASCIIMath/ASCIIMath_Rules.yaml +++ b/Rules/Braille/ASCIIMath/ASCIIMath_Rules.yaml @@ -223,7 +223,7 @@ # FIX: what should be output name: mhchem-hack # this captures the output for the mhchem's "<=>", "<<=>", and "<=>>" output (there are no Unicode arrows for them) - # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/NSoiffer/MathCAT/issues/60) + # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/daisy/MathCAT/issues/60) tag: mover match: - "*[1][substring(., 1, 1)='↽'] and" diff --git a/Rules/Braille/French/French_Rules.yaml b/Rules/Braille/French/French_Rules.yaml index 46c74a15c..f3514cebf 100644 --- a/Rules/Braille/French/French_Rules.yaml +++ b/Rules/Braille/French/French_Rules.yaml @@ -326,7 +326,7 @@ - name: mhchem-hack # this captures the output for the mhchem's "<=>", "<<=>", and "<=>>" output (there are no Unicode arrows for them) - # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/NSoiffer/MathCAT/issues/60) + # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/daisy/MathCAT/issues/60) tag: mover match: - "*[1][substring(., 1, 1)='↽'] and" diff --git a/Rules/Braille/LaTeX/LaTeX_Rules.yaml b/Rules/Braille/LaTeX/LaTeX_Rules.yaml index 315192dde..b9237dee0 100644 --- a/Rules/Braille/LaTeX/LaTeX_Rules.yaml +++ b/Rules/Braille/LaTeX/LaTeX_Rules.yaml @@ -235,7 +235,7 @@ # FIX: what should be output name: mhchem-hack # this captures the output for the mhchem's "<=>", "<<=>", and "<=>>" output (there are no Unicode arrows for them) - # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/NSoiffer/MathCAT/issues/60) + # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/daisy/MathCAT/issues/60) tag: mover match: - "*[1][substring(., 1, 1)='↽'] and" diff --git a/Rules/Braille/Nemeth/Nemeth_Rules.yaml b/Rules/Braille/Nemeth/Nemeth_Rules.yaml index 4fe821d0c..b5f74e562 100644 --- a/Rules/Braille/Nemeth/Nemeth_Rules.yaml +++ b/Rules/Braille/Nemeth/Nemeth_Rules.yaml @@ -164,7 +164,7 @@ replace: - test: if: "preceding-sibling::*" - then: [t: "W⣍"] # github.com/NSoiffer/MathCAT/issues/43#issuecomment-1297048039 + then: [t: "W⣍"] # github.com/daisy/MathCAT/issues/43#issuecomment-1297048039 - test: if: .[self::m:mlabeledtr] then: [x: "*[position()>1]"] @@ -900,7 +900,7 @@ - # this captures the output for the mhchem's "<=>", "<<=>", and "<=>>" output (there are no Unicode arrows for them) - # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/NSoiffer/MathCAT/issues/60) + # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/daisy/MathCAT/issues/60) name: chemistry-mhchem-equilibrium-arrow tag: mover match: diff --git a/Rules/Braille/Swedish/Swedish_Rules.yaml b/Rules/Braille/Swedish/Swedish_Rules.yaml index 7a8504574..94a40bdd6 100644 --- a/Rules/Braille/Swedish/Swedish_Rules.yaml +++ b/Rules/Braille/Swedish/Swedish_Rules.yaml @@ -329,7 +329,7 @@ - name: mhchem-hack # this captures the output for the mhchem's "<=>", "<<=>", and "<=>>" output (there are no Unicode arrows for them) - # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/NSoiffer/MathCAT/issues/60) + # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/daisy/MathCAT/issues/60) tag: mover match: - "*[1][substring(., 1, 1)='↽'] and" diff --git a/Rules/Braille/UEB/UEB_Rules.yaml b/Rules/Braille/UEB/UEB_Rules.yaml index 4d5b8ca1d..912e03d8a 100644 --- a/Rules/Braille/UEB/UEB_Rules.yaml +++ b/Rules/Braille/UEB/UEB_Rules.yaml @@ -300,7 +300,7 @@ - name: mhchem-hack # this captures the output for the mhchem's "<=>", "<<=>", and "<=>>" output (there are no Unicode arrows for them) - # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/NSoiffer/MathCAT/issues/60) + # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/daisy/MathCAT/issues/60) tag: mover match: - "*[1][substring(., 1, 1)='↽'] and" diff --git a/Rules/Braille/Vietnam/Vietnam_Rules.yaml b/Rules/Braille/Vietnam/Vietnam_Rules.yaml index 85146aeb7..60319160b 100644 --- a/Rules/Braille/Vietnam/Vietnam_Rules.yaml +++ b/Rules/Braille/Vietnam/Vietnam_Rules.yaml @@ -360,7 +360,7 @@ - # this captures the output for the mhchem's "<=>", "<<=>", and "<=>>" output (there are no Unicode arrows for them) - # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/NSoiffer/MathCAT/issues/60) + # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/daisy/MathCAT/issues/60) name: chemistry-mhchem-equilibrium-arrow tag: mover match: diff --git a/Rules/Intent/general.yaml b/Rules/Intent/general.yaml index d5ee239e9..15bd1dc7a 100644 --- a/Rules/Intent/general.yaml +++ b/Rules/Intent/general.yaml @@ -380,7 +380,7 @@ - # this captures the output for the mhchem's "<=>", "<<=>", and "<=>>" output (there are no Unicode arrows for them) - # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/NSoiffer/MathCAT/issues/60) + # this isn't a perfect match, but should be good enough and allows merging all three (see github.com/daisy/MathCAT/issues/60) name: chemistry-mhchem-equilibrium-arrow tag: mover match: diff --git a/docs/helpers_old.md b/docs/helpers_old.md index 5266c8b3e..9292a2557 100644 --- a/docs/helpers_old.md +++ b/docs/helpers_old.md @@ -5,7 +5,7 @@ This page is a work-in-progress. ## Getting Started If you plan to work on MathCAT development, you need to make use of github: -1. Fork the MathCAT repo at `github.com/NSoiffer/MathCAT` +1. Fork the MathCAT repo at `github.com/daisy/MathCAT` 2. Clone the forked copy so you have a local copy to work on. 3. Checkout the branch I create for your work (typically the country code for your translation) and work in that branch. diff --git a/docs/ru/helpers.md b/docs/ru/helpers.md index ebcd6e8c4..fa70fc86f 100644 --- a/docs/ru/helpers.md +++ b/docs/ru/helpers.md @@ -11,7 +11,7 @@ title: Руководство переводчика и разработчика ## Начало работы Если вы планируете участвовать в разработке MathCAT, используйте GitHub: -1. Создайте форк репозитория MathCAT `github.com/NSoiffer/MathCAT`. +1. Создайте форк репозитория MathCAT `github.com/daisy/MathCAT`. 2. Клонируйте форк, чтобы получить локальную копию для работы. 3. Переключитесь на созданную для вашей работы ветку, обычно названную кодом языка перевода, и работайте в ней. diff --git a/src/canonicalize.rs b/src/canonicalize.rs index 47be3ad00..0e0a81447 100644 --- a/src/canonicalize.rs +++ b/src/canonicalize.rs @@ -2083,7 +2083,7 @@ impl CanonicalizeContext { fn merge_number_blocks(context: &CanonicalizeContext, parent_mrow: Element, children: &mut Vec) { // debug!("parent:\n{}", mml_to_string(parent_mrow)); // If we find a comma that is not part of a number, don't form a number - // (see https://github.com/NSoiffer/MathCAT/issues/271) + // (see https://github.com/daisy/MathCAT/issues/271) // Unfortunately, we can't do this in the loop below because we might discover the "not part of a number" after a number has been formed let do_not_merge_comma = is_comma_not_part_of_a_number(children); let mut i = 0; diff --git a/src/chemistry.rs b/src/chemistry.rs index 5bec42313..173c5cccb 100644 --- a/src/chemistry.rs +++ b/src/chemistry.rs @@ -1653,7 +1653,7 @@ fn likely_chem_equation_operator(mathml: Element) -> i32 { return -3; // there is still a chance /// Detects output of mhchem for some equilibrium arrows that currently (11/22) don't have Unicode points - /// See github.com/NSoiffer/MathCAT/issues/60 for the patterns being matched + /// See github.com/daisy/MathCAT/issues/60 for the patterns being matched fn is_hack_for_missing_arrows(mover: Element) -> bool { assert_eq!(name(mover), "mover"); let children = mover.children(); diff --git a/src/navigate.rs b/src/navigate.rs index f893b3838..a3365ef56 100644 --- a/src/navigate.rs +++ b/src/navigate.rs @@ -798,7 +798,7 @@ fn key_press_to_command_and_param( // key press mapping should probably be stored externally (registry) with an app that allows changes // for now, we build in the defaults - // this is a hack to map alt+ctl+arrow to ctl+arrow to change table mappings (github.com/NSoiffer/MathCAT/issues/105) + // this is a hack to map alt+ctl+arrow to ctl+arrow to change table mappings (github.com/daisy/MathCAT/issues/105) // if this change sticks, choose_command() needs to be changed and this hack should go away let mut alt_key = alt_key; if alt_key && control_key && [VK_LEFT, VK_RIGHT, VK_UP, VK_DOWN].contains(&key) { diff --git a/src/speech.rs b/src/speech.rs index eaaeff282..100d5ff39 100644 --- a/src/speech.rs +++ b/src/speech.rs @@ -152,7 +152,7 @@ fn speak_rules(rules: &'static std::thread::LocalKey>, math // debug!("Speech string: {}", speech_string); // Note: [[...]] is added around a matching child, but if the "id" is on 'mathml', the whole string is used if !rules_with_context.nav_node_id.is_empty() { - // See https://github.com/NSoiffer/MathCAT/issues/174 for why we can just start the speech at the nav node + // See https://github.com/daisy/MathCAT/issues/174 for why we can just start the speech at the nav node let raw_intent_attr = mathml.attribute_value("data-intent-property"); let intent_attr = raw_intent_attr.as_deref().unwrap_or_default(); if let Some(start) = speech_string.find("[[") { diff --git a/src/xpath_functions.rs b/src/xpath_functions.rs index 19cf0b6cd..97e1db5ee 100644 --- a/src/xpath_functions.rs +++ b/src/xpath_functions.rs @@ -1945,7 +1945,7 @@ mod tests { test_is_not_simple("-x y z", "-x ⁢y⁢z")?; - test_is_not_simple("C(-2,1,4)", // github.com/NSoiffer/MathCAT/issues/199 + test_is_not_simple("C(-2,1,4)", // github.com/daisy/MathCAT/issues/199 "C(−2,1,4)")?; return Ok( () ); }); diff --git a/tests/braille/Nemeth/other.rs b/tests/braille/Nemeth/other.rs index 102c68096..5efae3d3d 100644 --- a/tests/braille/Nemeth/other.rs +++ b/tests/braille/Nemeth/other.rs @@ -5,7 +5,7 @@ use anyhow::Result; #[test] fn not_number_space_blocks() -> Result<()> { - // https://github.com/NSoiffer/MathCAT/issues/144 + // https://github.com/daisy/MathCAT/issues/144 let expr = "123 ⁣456"; test_braille("Nemeth", expr, "⠼⠂⠆⠒⠀⠼⠲⠢⠖")?; return Ok(()); @@ -14,7 +14,7 @@ fn not_number_space_blocks() -> Result<()> { #[test] fn space_between_digits() -> Result<()> { - // https://github.com/NSoiffer/MathCAT/issues/144 + // https://github.com/daisy/MathCAT/issues/144 let expr = "1 3 5"; test_braille("Nemeth", expr, "⠼⠂⠀⠼⠒⠀⠼⠢")?; return Ok(()); @@ -23,7 +23,7 @@ fn space_between_digits() -> Result<()> { #[test] fn space_hack_between_digits() -> Result<()> { - // https://github.com/NSoiffer/MathCAT/issues/144 + // https://github.com/daisy/MathCAT/issues/144 let expr = "1 ⁣3 ⁣5"; test_braille("Nemeth", expr, "⠼⠂⠀⠼⠒⠀⠼⠢")?; return Ok(()); @@ -32,7 +32,7 @@ fn space_hack_between_digits() -> Result<()> { #[test] fn tilde_prefix_bug_244() -> Result<()> { - // https://github.com/NSoiffer/MathCAT/issues/244 + // https://github.com/daisy/MathCAT/issues/244 let expr = " ~ p "; test_braille("Nemeth", expr, "⠈⠱⠏")?; return Ok(()); @@ -41,7 +41,7 @@ fn tilde_prefix_bug_244() -> Result<()> { #[test] fn double_struck_bug_334() -> Result<()> { - // https://github.com/NSoiffer/MathCAT/issues/334 -- double struck was problem (⠼ was missing); test all of the scripted numbers here + // https://github.com/daisy/MathCAT/issues/334 -- double struck was problem (⠼ was missing); test all of the scripted numbers here let expr = "𝟙,𝟐,𝟯,𝟺"; test_braille("Nemeth", expr, "⠠⠸⠼⠂⠠⠀⠸⠼⠆⠠⠀⠠⠨⠸⠼⠒⠠⠀⠼⠲")?; return Ok(()); @@ -50,7 +50,7 @@ fn double_struck_bug_334() -> Result<()> { #[test] fn extra_indicators_bug_343() -> Result<()> { - // https://github.com/NSoiffer/MathCAT/issues/343 -- extra indicators before baseline indicator due to -x^2, not there for x^2 + // https://github.com/daisy/MathCAT/issues/343 -- extra indicators before baseline indicator due to -x^2, not there for x^2 let expr = " @@ -78,7 +78,7 @@ fn extra_indicators_bug_343() -> Result<()> { #[test] fn find_baseline_indicator_bug_364() -> Result<()> { - // https://github.com/NSoiffer/MathCAT/issues/364 + // https://github.com/daisy/MathCAT/issues/364 let expr = r#" π diff --git a/tests/braille/Nemeth/rules.rs b/tests/braille/Nemeth/rules.rs index 18d31204f..d0cde7248 100644 --- a/tests/braille/Nemeth/rules.rs +++ b/tests/braille/Nemeth/rules.rs @@ -71,7 +71,7 @@ fn test_9_b_4() -> Result<()> { } #[test] fn test_9_c_1_linear() -> Result<()> { - // see https://github.com/NSoiffer/MathCAT/issues/43 for discussion on linear layout + // see https://github.com/daisy/MathCAT/issues/43 for discussion on linear layout let expr = "| 12 -3-4 @@ -1763,7 +1763,7 @@ fn racket_below_121_4() -> Result<()> { #[test] fn identity_matrix_126_linearize() -> Result<()> { - // see https://github.com/NSoiffer/MathCAT/issues/43 for discussion on linear layout + // see https://github.com/daisy/MathCAT/issues/43 for discussion on linear layout let expr = " ( 100 010 @@ -2203,7 +2203,7 @@ fn tensor_from_mathml_spec() -> Result<()> { l "; - // Note: the braille answer was verified to be correct (see https://github.com/NSoiffer/MathCAT/issues/55) + // Note: the braille answer was verified to be correct (see https://github.com/daisy/MathCAT/issues/55) test_braille("Nemeth", expr, "⠠⠗⠰⠊⠐⠘⠚⠐⠰⠅⠐⠰⠇")?; return Ok(()); } diff --git a/tests/braille/UEB/iceb.rs b/tests/braille/UEB/iceb.rs index b7f3a6560..3bd48b19b 100644 --- a/tests/braille/UEB/iceb.rs +++ b/tests/braille/UEB/iceb.rs @@ -522,7 +522,7 @@ fn ratio_3_2_6() -> Result<()> { #[test] fn standing_alone_1() -> Result<()> { - // Tests bug: github.com/NSoiffer/MathCAT/issues/142 + // Tests bug: github.com/daisy/MathCAT/issues/142 let expr = "(n=7)"; test_braille("UEB", expr, "⠐⠣⠰⠝⠀⠐⠶⠀⠼⠛⠐⠜")?; return Ok(()); diff --git a/tests/braille/UEB/other.rs b/tests/braille/UEB/other.rs index f1beb55cc..7dda38f6c 100644 --- a/tests/braille/UEB/other.rs +++ b/tests/braille/UEB/other.rs @@ -72,7 +72,7 @@ fn number_space_before_and_after() -> Result<()> { #[test] fn not_number_space_blocks() -> Result<()> { - // https://github.com/NSoiffer/MathCAT/issues/144 + // https://github.com/daisy/MathCAT/issues/144 let expr = "123 ⁣456"; test_braille("UEB", expr, "⠼⠁⠃⠉⠀⠼⠙⠑⠋")?; return Ok(()); @@ -81,7 +81,7 @@ fn not_number_space_blocks() -> Result<()> { #[test] fn dot_above_bug_204() -> Result<()> { - // https://github.com/NSoiffer/MathCAT/issues/204 + // https://github.com/daisy/MathCAT/issues/204 let expr = " 0. 6 · "; test_braille("UEB", expr, "⠼⠚⠲⠣⠼⠋⠜⠘⠲")?; return Ok(()); @@ -90,7 +90,7 @@ fn dot_above_bug_204() -> Result<()> { #[test] fn tilde_prefix_bug_244() -> Result<()> { - // https://github.com/NSoiffer/MathCAT/issues/244 + // https://github.com/daisy/MathCAT/issues/244 let expr = " ~ p "; test_braille("UEB", expr, "⠈⠔⠏")?; return Ok(()); @@ -109,7 +109,7 @@ fn double_tilde_prefix_bug_244() -> Result<()> { #[test] fn space_hack_between_digits() -> Result<()> { - // https://github.com/NSoiffer/MathCAT/issues/144 + // https://github.com/daisy/MathCAT/issues/144 let expr = "1 ⁣3 ⁣5"; test_braille("UEB", expr, "⠼⠁⠀⠼⠉⠀⠼⠑")?; return Ok(()); @@ -118,7 +118,7 @@ fn space_hack_between_digits() -> Result<()> { #[test] fn space_hack_around_operator() -> Result<()> { - // https://github.com/NSoiffer/MathCAT/issues/144 + // https://github.com/daisy/MathCAT/issues/144 let expr = "y ⁣= ⁣5"; test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠼⠑")?; return Ok(()); From 72e2b1ddf81950d2f12c6fd6cb2aadcdf4df8dac Mon Sep 17 00:00:00 2001 From: natalieac03 Date: Wed, 2 Sep 2026 10:04:44 -0300 Subject: [PATCH 083/120] Add Brazilian Portuguese (pt) translation- #729 (#736) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create test * Traducao das regras pt-BR: unicode, definitions, SimpleSpeak, SharedRules e testes * Versão inicial em português do MathCAT * Corrige bugs da auditoria e traduz unicode-full.yaml (acentos, setas, operadores, formas) * Regras da rodada de testes: probability, ignore_period, unicode-full * Corrige unidades sec/arcsec, remove dot-over espúria, ACHADOS.md * pt: corrige 7 bugs de regra e reconcilia os testes (92/92) cargo test Languages::pt saía de 33 falhas em 91 testes. Agora passa 92/92, e a suíte inteira do repositório continua verde. Nada em src/ foi tocado. Bugs de regra: - SharedRules/general.yaml: a distinção Verbose/Medium tinha desabado na química. 14 pares de ramos diziam "subscrito"/"sobrescrito" nos dois níveis, onde o inglês tem subscript/sub. Um deles (ramo $Prescripts[4]) dizia "subscrito" onde o inglês diz superscript. - SharedRules/general.yaml: ligação dupla escrita "::" não era reconhecida. A regra testava .='::', mas o canonicalizador converte para ∷ (U+2237) antes de as regras rodarem, então H₂C::CH₂ saía "maiúsculo c, como maiúsculo c". - unicode.yaml: a regra de "|" era uma simplificação da inglesa e perdia três casos — P(A|B) ("dado"), a|b entre números ("divide") e o intent literal. Portada a estrutura inglesa inteira. - SharedRules/general.yaml: fora da química caía no default-text, que fala o nome da tag, e saía a palavra inglesa "none". Agora "nenhum". - SharedRules/general.yaml: faltava o Log maiúsculo (valor principal), que saía "Log de x". Marcado t: — a tradução precisa de conferência. - definitions.yaml: "segundo de arco" estava duplicado em PluralForms. O leitor de YAML do Rust aceita, mas audit-translations recusava o arquivo inteiro, ou seja, definitions.yaml não estava sendo auditado de verdade. - definitions.yaml: plurais das formas prefixadas de "segundo de arco", que a regra genérica produzia como "yoctossegundo de arcos". Testes desatualizados: pausas geradas pelo motor (ver ACHADOS.md seção 1), o artigo em "o log de x", "1 meio" em vez de "um meio", "l n x" no modo conciso, e a vírgula do grego (que o teste inglês também tem). Decisão: menos unário fica "menos", não "negativo". Os testes estavam divididos; a regra já dizia "menos" e os três discordantes foram alinhados. Resolve duas pendências do ACHADOS.md: a contradição aparente entre tensor_mmultiscripts e mhchem_so4_2mais (caminhos de código diferentes — só o da química estava quebrado) e a divergência de inverse_trig entre SimpleSpeak e ClearSpeak (as regras já estavam alinhadas; era o teste). ACHADOS.md ganha a seção 6. Corrigidas duas afirmações das seções anteriores: compute_auto_pause conta bytes, não letras; e as regras de química não eram idênticas às inglesas (só as diretivas de pausa eram). Registrado também que trocar bytes por caracteres foi testado e não resolve — piora de 15 para 36 falhas. Co-Authored-By: Claude Opus 5 * Terminologia: maiúscula, romanos por valor, expoente zero cardinal, testes * pt: pertinência sem verbo finito dentro de conjunto; ∉ com In deixa de colidir com ⊄ Duas correções de fala, com varredura e não caso a caso (ACHADOS 7.8/7.9): 1. Dentro de um conjunto ({x ∈ ℤ : x > 5}) a expressão modifica "todos os x" e não pode ter verbo finito nem singular: o verbo grudava em "conjunto" ("o conjunto de todos os x pertence a inteiros"). Seis dos oito ramos estavam errados, não só o Belongs. Agora: em / membros de / elementos de / pertencentes a, e as negações nominais. "fora de" foi rejeitado por trocar pertinência por localização; "não em" por ser truncado. O ramo SimpleSpeak usava uma string só para dentro e fora e passou a bifurcar. 2. ∉ com ClearSpeak_SetMemberSymbol=In fora de conjunto falava "não está contido em", exatamente a fala do ⊄. Passa a "não está em", espelhando o ∈. Também nesta rodada, já em varredura: termo único por família de estilo (fraktur, caligráfico) e adjetivo posposto nos dois arquivos; ∈ ∊ com Auto separado de Member como o ∉; ! volta a distinguir literal/fatorial; ≇ ≚ ≞ ⋕ com termo matemático em vez do nome Unicode; cópula dupla em ∦ ⊈ ⊉. Novo: tests/Languages/pt/ClearSpeak/sets.rs (pt vai de 102 para 142 testes, ∈ e ∊ travados iguais nas cinco preferências e nos dois estilos), PythonScripts/conferir_vocabulario.py (consistência de vocabulário entre unicode.yaml e unicode-full.yaml: 8 -> 4 grupos, restantes benignos) e Rules/Languages/pt/TERMINOLOGIA_PT_BR.md (registro único das leituras). Suíte inteira: 7307 passed, 0 failed. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_0134dJm6wzqbquvZrNhPD3Ea * pt: corrige o que o material real do ACESSÍLIA exige; recuo controlado; pacote de escuta Diagnóstico antes de escolher (ACHADOS 9): o MathML do ACESSÍLIA vem do latex2mathml nos dois caminhos (inclusive Docling) e nunca traz intent=, então os 186 IntentMappings ausentes não são prioridade. O que importa é o que o motor infere sozinho e o que sai cru para o sintetizador. Correções, pelo dano ao estudante: - caracteres crus no corpus real: negrito latino e grego (\mathbf, \boldsymbol), ⟨ ⟩, ¯ de \bar{x} (o motor canonicaliza U+0304/U+203E para U+00AF, então a entrada "com mácron" nunca era alcançada), setas longas, µ Ω ℧ Å soltos - "1 terceiro" -> "1 terço": listas fracionárias paravam em "meio" - "o integral" -> "a integral": artigo concorda com o operador grande - "magnitude de x" -> "a norma de x": intent inferido sem mapping pt - "cross product"/"dot product" literais -> "produto vetorial/escalar": o intent inferido vem sem filhos e nenhuma regra casava com count(*)=0 - recuo controlado só em regra (gerar_recuo_en.py): 2091 entradas do en para codepoints sem cobertura, estilo já definido traduzido, resto em t: minúsculo. Antes o caractere saía cru; agora sai em inglês, contado como pendente. Pacote da sessão de escuta (gerar_pacote_escuta.py -> SESSAO_ESCUTA.md): 30 expressões em 6 áreas, fala real nos dois estilos e três verbosidades, roteiro e tabela das decisões que dependem do ouvinte. cargo test: 7307 passed, 0 failed (pt: 142). conferir_vocabulario.py: 4 grupos, todos benignos, antes e depois. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_0134dJm6wzqbquvZrNhPD3Ea * pt: P(A ∣ B) fala "dado"; varredura de vazamento nas 593 expressões do en O latex2mathml emite \mid como U+2223, não "|": P(A ∣ B) saía "A divide B". A entrada "∣" ganha a mesma condição de contexto da regra do "|" (dentro de P( · ∣ · ) é "dado"; fora continua "divide"). Em conjunto por compreensão {x ∣ ...} o problema persiste e não é regra: o canonicalizador só trata "|" como cerca (registrado como item de src/ em ACHADOS 9.6). Varredura: as 593 expressões dos testes do inglês faladas em português, à procura de palavras inglesas. Fora dos em inglês dos próprios testes e das letras decoradas, nenhum vazamento em material real. As decorações (circulado, entre parênteses, com ponto, invertido) entraram no mapa de estilo do recuo controlado. cargo test: 7307 passed, 0 failed (pt: 142). conferir_vocabulario.py: 4. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_0134dJm6wzqbquvZrNhPD3Ea * pt: recuo marcado # RECUO-EN e separado da tradução pendente; regressão do recuo = 0 As entradas do recuo controlado usavam t: minúsculo, a mesma marca de "traduzido mas não conferido" — 2 mil delas contra 20 pendências reais, que sumiam no meio. Cada entrada do bloco leva agora # RECUO-EN na linha da chave e o conferir_vocabulario.py ganhou a seção 5, que reporta os dois números em separado: 2091 entradas de recuo (1980 strings em inglês) contra 20 strings pendentes de conferência (fraktur x10, caligráfico x10). Verificação de regressão: as 593 expressões dos testes do en faladas em pt sem e com o bloco. 526 iguais; 67 mudaram, todas com caractere cru antes. Nenhuma expressão 100% português mudou de fala. cargo test: 7307 passed, 0 failed (pt: 142). conferir_vocabulario.py: 4. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_0134dJm6wzqbquvZrNhPD3Ea * Delete auditar_pt.sh * Delete Rules/Languages/pt/ACHADOS.md * Delete Rules/Languages/pt/SESSAO_ESCUTA.md * Delete Rules/Languages/pt/TERMINOLOGIA_PT_BR.md * Terminologia: maiúscula, romanos por valor, expoente zero cardinal, testes * Restaura arquivos apagados por engano * Delete venv directory * Delete PythonScripts/venv directory * Delete PythonScripts/gerar_recuo_en.py * Delete PythonScripts/gerar_pacote_escuta.py * Delete PythonScripts/conferir_vocabulario.py * Remove translation correction comments from YAML Removed comments regarding translation corrections and pluralization issues. * Clean up comments in ClearSpeak_Rules.yaml Removed comments regarding corrections in Portuguese grammar and terminology for mathematical expressions. * Clean up comments in SimpleSpeak_Rules.yaml Removed comments regarding corrections related to gendered ordinal numbers and unnecessary English suffixes. * Clean up translation notes in definitions.yaml Removed translation notes and comments from definitions.yaml. * Fix tag for overview-default in overview.yaml Corrected the tag for 'overview-default' to include 'fraction' for better intent matching. * Clean up comments in escuta.rs Removed comments and documentation from escuta.rs. * Update expressions in escuta_expressoes.tsv * Clean up comments in intent.rs Removed cautionary comments regarding the Spanish equivalent tests and their historical inaccuracies. * Remove venv from version control * Restringe o PR a Rules/Languages/pt e tests/Languages/pt Reverte o .gitignore à versão do upstream; arquivos locais (venv, saídas de teste, scripts auxiliares e documentos .md) ficam fora do controle de versão via .git/info/exclude. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01VJN43HMwJHeagwCporXvF3 --------- Co-authored-by: Claude Opus 5 --- Rules/Languages/pt/ClearSpeak_Rules.yaml | 801 ++++ Rules/Languages/pt/SharedRules/calculus.yaml | 61 + Rules/Languages/pt/SharedRules/default.yaml | 734 ++++ Rules/Languages/pt/SharedRules/general.yaml | 1121 ++++++ Rules/Languages/pt/SharedRules/geometry.yaml | 80 + .../pt/SharedRules/linear-algebra.yaml | 35 + Rules/Languages/pt/SimpleSpeak_Rules.yaml | 338 ++ Rules/Languages/pt/definitions.yaml | 496 +++ Rules/Languages/pt/navigate.yaml | 1773 +++++++++ Rules/Languages/pt/overview.yaml | 119 + Rules/Languages/pt/unicode-full.yaml | 3431 +++++++++++++++++ Rules/Languages/pt/unicode.yaml | 571 +++ tests/Languages/pt.rs | 17 + tests/Languages/pt/ClearSpeak/sets.rs | 398 ++ tests/Languages/pt/SimpleSpeak/functions.rs | 422 ++ tests/Languages/pt/alphabets.rs | 438 +++ tests/Languages/pt/chemistry.rs | 721 ++++ tests/Languages/pt/escuta.rs | 33 + tests/Languages/pt/escuta_expressoes.tsv | 30 + tests/Languages/pt/intent.rs | 72 + tests/Languages/pt/shared.rs | 346 ++ tests/Languages/pt/unicode_full.rs | 159 + tests/Languages/pt/units.rs | 411 ++ tests/languages.rs | 1 + 24 files changed, 12608 insertions(+) create mode 100644 Rules/Languages/pt/ClearSpeak_Rules.yaml create mode 100644 Rules/Languages/pt/SharedRules/calculus.yaml create mode 100644 Rules/Languages/pt/SharedRules/default.yaml create mode 100644 Rules/Languages/pt/SharedRules/general.yaml create mode 100644 Rules/Languages/pt/SharedRules/geometry.yaml create mode 100644 Rules/Languages/pt/SharedRules/linear-algebra.yaml create mode 100644 Rules/Languages/pt/SimpleSpeak_Rules.yaml create mode 100644 Rules/Languages/pt/definitions.yaml create mode 100644 Rules/Languages/pt/navigate.yaml create mode 100644 Rules/Languages/pt/overview.yaml create mode 100644 Rules/Languages/pt/unicode-full.yaml create mode 100644 Rules/Languages/pt/unicode.yaml create mode 100644 tests/Languages/pt.rs create mode 100644 tests/Languages/pt/ClearSpeak/sets.rs create mode 100644 tests/Languages/pt/SimpleSpeak/functions.rs create mode 100644 tests/Languages/pt/alphabets.rs create mode 100644 tests/Languages/pt/chemistry.rs create mode 100644 tests/Languages/pt/escuta.rs create mode 100644 tests/Languages/pt/escuta_expressoes.tsv create mode 100644 tests/Languages/pt/intent.rs create mode 100644 tests/Languages/pt/shared.rs create mode 100644 tests/Languages/pt/unicode_full.rs create mode 100644 tests/Languages/pt/units.rs diff --git a/Rules/Languages/pt/ClearSpeak_Rules.yaml b/Rules/Languages/pt/ClearSpeak_Rules.yaml new file mode 100644 index 000000000..634aaf0ee --- /dev/null +++ b/Rules/Languages/pt/ClearSpeak_Rules.yaml @@ -0,0 +1,801 @@ +- 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: "menos" + - x: "translate(text(), '-_', '')" + +- name: default + tag: square-root + match: "." + replace: + - test: + if: "$Verbosity!='Terse'" + then: [T: "a"] # "a raiz" (feminino) + - T: "raiz quadrada" + # CORREÇÃO de ordem: o adjetivo vem depois do substantivo em português + # "a raiz quadrada negativa/positiva de x", não "a positiva raiz quadrada". + - test: + if: $ClearSpeak_Roots = 'PosNegSqRoot' or $ClearSpeak_Roots = 'PosNegSqRootEnd' + then: + - bookmark: "*[1]/@id" + - test: + if: parent::*[self::m:minus and count(*)=1] + then: [T: "negativa"] + else: [T: "positiva"] + - test: + if: "$Verbosity!='Terse'" + then: [T: "de"] + else: [pause: short] + - x: "*[1]" + - test: + - if: "$ClearSpeak_Roots = 'RootEnd' or $ClearSpeak_Roots = 'PosNegSqRootEnd'" + then: + - pause: short + - T: "fim da raiz" + - pause: medium + - else_if: "IsNode(*[1], 'simple')" + then: [pause: short] + else: [pause: long] + +- name: default + # audit-ignore: divergência deliberada do en — adjetivo positiva/negativa pós-posto (ordem do português) + tag: root + match: "." + replace: + - test: + if: "$Verbosity!='Terse'" + then: [T: "a"] + - test: + if: "*[2][self::m:mn]" + then_test: + - if: "*[2][.='2']" + then: [T: "raiz quadrada"] + - else_if: "*[2][.='3']" + then: [T: "raiz cúbica"] + - else_if: "*[2][not(contains(., '.'))]" + then: [T: "raiz de índice", x: "*[2]"] + else: + - T: "raiz de índice" + - x: "*[2]" + # adjetivo depois do substantivo: "a raiz cúbica negativa de x" + - 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: "negativa"] + else: [T: "positiva"] + - test: + if: "$Verbosity!='Terse'" + then: [T: "de"] + - x: "*[1]" + - test: + if: $ClearSpeak_Roots = 'RootEnd' or $ClearSpeak_Roots = 'PosNegSqRootEnd' + then: + - pause: short + - T: "fim da raiz" + - 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: "menos"] + else: [T: "positivo"] + - 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" # 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: "por" # "cinco metros por segundo" + - 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(., '.')) and ($ClearSpeak_Fractions='Ordinal' or text()<20)] and" + - "*[2][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or (2<= text() and text()<=10))]" + replace: [x: ToCommonFraction(.)] + +- name: common-fraction-mixed-number + tag: fraction + match: + - "preceding-sibling::*[1][self::m:mo][.='⁤'] and" # preceding element is invisible plus + - "($ClearSpeak_Fractions='Auto' or $ClearSpeak_Fractions='Ordinal' or $ClearSpeak_Fractions='EndFrac') and" + - "*[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))]" + replace: [x: ToCommonFraction(.)] + +- 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: "a"] + - T: "fração" + - x: "*[1]" + # léxico = fração simples é "a sobre b", não "a partido por b" + - T: "sobre" + - x: "*[2]" + - test: + + 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: "fim da fração" + - 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: "*[1]" + - T: "sobre" + - x: "*[2]" + - test: + if: "$ClearSpeak_Fractions='EndFrac' or $ClearSpeak_Fractions='OverEndFrac'" + then: + - pause: short + - T: "fim da fração" + - pause: short + +- name: default + tag: fraction + match: "." + replace: + - OT: "a" + - T: "fração com numerador" + - test: + if: not(IsNode(*[1], 'simple')) + then: [pause: medium] + - x: "*[1]" + - pause: medium + - T: "e denominador" + - x: "*[2]" + - pause: long + - test: + if: "$ClearSpeak_Fractions='EndFrac' or $ClearSpeak_Fractions='GeneralEndFrac'" + then: + - pause: short + - T: "fim da fração" + - 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: [x: "*[1]", bookmark: "*[2]/@id", T: "inverso"] + else_test: + if: $ClearSpeak_Trig = 'ArcTrig' + then: [bookmark: "*[2]/@id", T: "arco", x: "*[1]"] + else: [bookmark: "*[2]/@id", T: "inversa de", x: "*[1]"] # default/Auto + +- name: function-squared-or-cubed + # audit-ignore: divergência deliberada do en — ao quadrado/ao cubo com par verboso padronizado + 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_test: + if: "$Verbosity='Verbose'" + then: [T: "elevado ao quadrado"] + else: [T: "ao quadrado"] + else: [T: "ao cubo"] +- name: function-power + # audit-ignore: divergência deliberada do en — forma cardinal no lugar de ToOrdinal (sem ordinais femininos no motor) + tag: power + match: + - "following-sibling::*[1][self::m:mo][.='⁡']" #invisible function apply + replace: + - test: + if: "$Verbosity!='Terse'" + then: [T: "a"] + - bookmark: "*[2]/@id" + - T: "potência" + - x: "*[2]" + - T: "de" + - pause: short + - x: "*[1]" + +- 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: "elevado ao expoente" + - pause: short + - x: "*[2]" + - pause: short + - T: "fim do expoente" + +- name: AfterPower-default + tag: power + match: "$ClearSpeak_Exponents = 'AfterPower'" + replace: + - x: "*[1]" + - T: "elevado à potência" + - x: "*[2]" + - pause: short + +- name: squared + tag: power + match: "*[2][self::m:mn][.='2'] and $ClearSpeak_Exponents = 'Auto'" + replace: + - x: "*[1]" + - bookmark: "*[2]/@id" + - T: "ao quadrado" + +- name: cubed + tag: power + match: "*[2][self::m:mn][.='3'] and $ClearSpeak_Exponents = 'Auto'" + replace: + - x: "*[1]" + - bookmark: "*[2]/@id" + - T: "ao cubo" + +- name: simple-integer + # audit-ignore: divergência deliberada do en — forma cardinal; preferência Ordinal degrada documentadamente + tag: power + match: "*[2][self::m:mn][not(contains(., '.'))]" + replace: + - x: "*[1]" + - T: "elevado a" + - x: "*[2]" + +- name: simple-negative-integer + # audit-ignore: divergência deliberada do en — forma cardinal + tag: power + match: + - "*[2][self::m:minus and count(*)=1 and" + - " *[1][self::m:mn][not(contains(., '.'))]" + - " ]" + replace: + - x: "*[1]" + - T: "elevado a" + - x: "*[2]" + +- name: simple-var + # audit-ignore: divergência deliberada do en — sem o sufixo fonético inglês -th + tag: power + match: "*[2][self::m:mi][string-length(.)=1]" + replace: + # CORREÇÃO: removido o bloco "pronounce" com o sufixo inglês "-th" (IPA θ), + # que produzia som estrangeiro. "x elevado a n" já está completo. + - x: "*[1]" + - T: "elevado a" + - x: "*[2]" + +# 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: "elevado à potência" + - x: "*[2]" + +- # - [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: "elevado à potência" + - x: "*[2]" + +- # [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: "elevado à potência" + - x: "*[2]" + +- # -[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: "elevado à potência" + - x: "*[2]" + +- 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: "elevado ao expoente" + - pause: short + - x: "*[2]" + - pause: short + - T: "fim do expoente" + +- name: default + tag: power + match: "." + replace: + - x: "*[1]" + - T: "elevado à potência" + - x: "*[2]" + +# +# Some rules on mrows +# +- # the inference rules lump absolute value and cardinality together, so those rules are implemented here + name: ClearSpeak-absolute-value + # audit-ignore: divergência deliberada do en — três variáveis por causa do gênero (a cardinalidade / o valor absoluto) + tag: absolute-value + match: "." + variables: + - WordToSay: "IfThenElse($ClearSpeak_AbsoluteValue = 'Cardinality', 'cardinalidade', 'valor absoluto')" + - ArticleToSay: "IfThenElse($ClearSpeak_AbsoluteValue = 'Cardinality', 'a', 'o')" + - EndToSay: "IfThenElse($ClearSpeak_AbsoluteValue = 'Cardinality', 'fim da cardinalidade', 'fim do valor absoluto')" + replace: + - test: + if: "$Verbosity!='Terse'" + then: [x: "$ArticleToSay"] + - x: "$WordToSay" + - T: "de" + - x: "*[1]" + - test: + if: "$ClearSpeak_AbsoluteValue = 'AbsEnd'" + then: + - pause: short + - x: "$EndToSay" + - pause: short + +- name: set + # audit-ignore: divergência deliberada do en — conjunto de todos os x tal que (uso brasileiro) + tag: set + match: "." + replace: + - test: + - if: "count(*)=0" + then: [T: "o conjunto vazio"] + - else_if: "count(*)=2" + then: + - test: + if: "$Verbosity!='Terse'" + then: [T: "o"] + - T: "conjunto vazio" + - else_if: "count(*[1]/*)=3 and *[1]/*[2][self::m:mo][.=':' or .='|' or .='∣']" + then: + - test: + if: "$Verbosity!='Terse'" + then: [T: "o"] + # "conjunto de todos os x tal que" o inglês é + # "set of all"; a preferência woAll suprime só o "todos os". + - T: "conjunto de" + - test: + if: $ClearSpeak_Sets != 'woAll' + then: [T: "todos os"] + - x: "*[1]/*[1]" + - T: "tal que" + - x: "*[1]/*[3]" + else: + - test: + if: $ClearSpeak_Sets != 'SilentBracket' + then: + - test: + if: "$Verbosity!='Terse'" + then: [T: "o"] + - T: "conjunto" + - x: "*[1]" + +- # 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 count(*)=1 and *[1][.='∞']]" + - is_intervals_end_infinity: "*[2][.='∞'or (self::m:plus and count(*)=1 and *[1][.='∞'])]" + tag: [open-interval, open-closed-interval, closed-interval, closed-open-interval] + match: "." + replace: + - T: "o intervalo de" + - x: "*[1]" + - T: "a" + - x: "*[2]" + - pause: short + - test: + if: "not($is_intervals_start_infinity)" + then: + - test: + if: "starts-with(name(.), 'open')" + # CORREÇÃO: "no" é espanhol; em português, "não" + then: [T: "não"] + - T: "incluindo" + - 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: "ou"] + - else_if: "name(.)='closed-interval'" + then: [T: "e"] + else: [T: "mas"] + # 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: "não"] + - T: "incluindo" + - 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: "escolhe" + - x: "*[2]/*[1]/*" + +- name: ClearSpeak-default + tag: [mtr, mlabeledtr] + match: "parent::m:matrix or parent::m:determinant" + replace: + - pause: medium + - T: "linha" + - x: "count(preceding-sibling::*)+1" + - test: + if: .[self::m:mlabeledtr] + then: + - T: "com rótulo" + - x: "*[1]/*" + - pause: short + - pause: medium + - test: + if: .[self::m:mlabeledtr] + then: [x: "*[position()>1]"] + else: [x: "*"] + +- name: matrix-default + tag: mtd + match: "parent::*[parent::m:matrix or parent::m:determinant]" + 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: "coluna" + - x: "count(preceding-sibling::*)+1" + - pause: medium + - x: "*" + - test: + # short pause after each element; medium pause if last element in a row; long pause for last element in matrix + - if: count(following-sibling::*) > 0 + then: [pause: short] + - else_if: count(../following-sibling::*) > 0 + then: [pause: medium] + else: [pause: long] + +- # 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: [T: "o"] + - test: + - if: ".='log'" + then: [T: "log"] + - else_if: "$ClearSpeak_Log = 'LnAsNaturalLog'" + then: [T: "logaritmo natural"] # phrase(the 'natural log' of x) + else: [spell: "'ln'"] + +- name: ClearSpeak-multi-line + # audit-ignore: divergência deliberada do en — plural por cadeias singular/plural, não por concatenação de s + tag: [piecewise, system-of-equations, lines] # these are ignored in favor of the ClearSpeak prefs + match: "." + variables: [IsColumnSilent: true()] + replace: + - x: "count(*)" + - test: + - if: $ClearSpeak_MultiLineOverview = 'Auto' + then: + - test: + if: "count(*) > 1" + then: + - test: + - if: "($ClearSpeak_MultiLineLabel = 'Auto' and self::m:piecewise) or $ClearSpeak_MultiLineLabel = 'Case'" + then: [T: "casos"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Auto' or $ClearSpeak_MultiLineLabel = 'Line'" + then: [T: "linhas"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Constraint'" + then: [T: "restrições"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Equation'" + then: [T: "equações"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Row'" + then: [T: "linhas"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Step'" + then: [T: "passos"] + # else 'None' — não diz nada + else: + - test: + - if: "($ClearSpeak_MultiLineLabel = 'Auto' and self::m:piecewise) or $ClearSpeak_MultiLineLabel = 'Case'" + then: [T: "caso"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Auto' or $ClearSpeak_MultiLineLabel = 'Line'" + then: [T: "linha"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Constraint'" + then: [T: "restrição"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Equation'" + then: [T: "equação"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Row'" + then: [T: "linha"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Step'" + then: [T: "passo"] + # else 'None' — não diz nada + - pause: short + - x: "*" + +- name: ClearSpeak-default-multiline + # audit-ignore: divergência deliberada do en — rótulos com plural correto + tag: [mtr, mlabeledtr] + match: "parent::m:piecewise or parent::m:system-of-equations or parent::m:lines" + replace: + - test: + - if: "($ClearSpeak_MultiLineLabel = 'Auto' and parent::m:piecewise) or $ClearSpeak_MultiLineLabel = 'Case'" + then: [T: "caso"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Auto' or $ClearSpeak_MultiLineLabel = 'Line'" + then: [T: "linha"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Constraint'" + then: [T: "restrição"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Equation'" + then: [T: "equação"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Row'" + then: [T: "linha"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Step'" + then: [T: "passo"] + # else 'None' — não diz nada + - test: + if: "$ClearSpeak_MultiLineLabel != 'None'" + then: + - x: "count(preceding-sibling::*)+1" + - test: + if: .[self::m:mlabeledtr] + then: + - T: "com rótulo" + - x: "*[1]/*" + - pause: medium + - test: + if: .[self::m:mlabeledtr] + then: [x: "*[position()>1]"] + else: [x: "*"] + - test: + if: "$ClearSpeak_MultiLineLabel != 'None'" + then: [pause: long] + +- 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: ""] + # léxico = "vezes" é a forma padrão; "multiplicado por" fica + # para o modo verboso + else_test: + if: "$Verbosity = 'Verbose'" + then: [T: "multiplicado por"] + else: [T: "vezes"] + +- 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 + # audit-ignore: divergência deliberada do en — vezes/multiplicado por conforme verbosidade (léxico ACESSÍLIA) + 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 (" + - " $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" # followed by parens + - " ( (self::m:msup or self::m:msub or self::m:msubsup or self::m:power) and " # followed by msup, etc. + - " *[1][self::m:mrow[IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|')] or " + - " self::m:matrix or self::m:determinant]" # base has parens + - " )" + - " ]" + # 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: + - test: + if: "$Verbosity = 'Verbose'" + then: [T: "multiplicado por"] + else: [T: "vezes"] + +- name: no-say-parens + # audit-ignore: divergência deliberada do en — idem ClearSpeak-times + 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') )" + # 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/pt/SharedRules/calculus.yaml b/Rules/Languages/pt/SharedRules/calculus.yaml new file mode 100644 index 000000000..03b99253a --- /dev/null +++ b/Rules/Languages/pt/SharedRules/calculus.yaml @@ -0,0 +1,61 @@ +--- + +- name: divergence + tag: divergence + match: "count(*) = 1" + replace: + # CORREÇÃO: a polaridade estava invertida — o modo padrão (Medium) ouvia a + # abreviação "div"; o nome completo só saía no Verbose. No inglês, só o + # Terse abrevia. E o "de" era incondicional, contrariando TerseFunctionNames + # (divergence/curl/gradient não dizem "de" no modo conciso). + - test: + if: "$Verbosity='Terse'" + then: [T: "div"] + else: [T: "divergente de"] + - test: + if: "not(IsNode(*[1], 'leaf'))" + then: [pause: short] + - x: "*[1]" + +- name: curl + tag: curl + match: "count(*) = 1" + replace: + - T: "rotacional" # phrase(the 'curl of' a field) + - test: + if: "$Verbosity!='Terse'" + then: [T: "de"] + - test: + if: "not(IsNode(*[1], 'leaf'))" + then: [pause: short] + - x: "*[1]" + +- name: gradient + tag: gradient + match: "count(*) = 1" + replace: + - test: + if: "$Verbosity!='Terse'" + then: [T: "gradiente de"] # phrase('divergence' from the mean) + else: [T: "nabla"] # phrase('divergence' from the mean) + - test: + if: "not(IsNode(*[1], 'leaf'))" + then: [pause: short] + - x: "*[1]" + + +- name: laplacian + tag: laplacian + match: "count(*) <= 1" # can be on ∇^2 or on enclosing mrow + replace: + - T: "laplaciano" + - test: + if: "count(*) = 1" + then: + - test: + if: "$Verbosity!='Terse'" + then: [T: "de"] + - test: + if: "not(IsNode(*[1], 'leaf'))" + then: [pause: short] + - x: "*[1]" diff --git a/Rules/Languages/pt/SharedRules/default.yaml b/Rules/Languages/pt/SharedRules/default.yaml new file mode 100644 index 000000000..ff8ed2cf8 --- /dev/null +++ b/Rules/Languages/pt/SharedRules/default.yaml @@ -0,0 +1,734 @@ +--- +#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)" + # 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" + # ACRESCENTADO (en): numerais romanos são soletrados ("XIV" -> letra a + # letra), não lidos como palavra. + - 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()"] + # ACRESCENTADO (en): NavMode=Character precisa disto + - else_if: "@data-chem-element or @data-roman-numeral" + then: [spell: "text()", pause: "short"] + else: [x: "translate(., '-_\u00A0', ' ')"] # from intent literals + +- name: default + tag: ms + match: "." + replace: + - T: "a cadeia" + - pause: short + - x: "text()" + +- name: default + tag: mstyle + match: "." + replace: [x: "*"] + + +- name: literal-simple + # don't include nested fractions. E.g, fraction a plus b over c + 1 end fraction" is ambiguous + # by simplistic SimpleSpeak's rules "b over c" is a fraction, but if we say nested fractions + # are never simple, then any 'over' applies only to enclosing "fraction...end fraction" pair. + tag: mfrac + match: + - "(IsNode(*[1],'leaf') and IsNode(*[2],'leaf')) and" + - "not(ancestor::*[name() != 'mrow'][1]/self::m:fraction)" # FIX: can't test for mrow -- what should be used??? + replace: + - x: "*[1]" + - T: "sobre" + - x: "*[2]" + - pause: short + +- name: literal-default + tag: mfrac + match: "." + replace: + - T: "começar" + - pause: short + - x: "*[1]" + - test: + if: "not(IsNode(*[1],'leaf'))" + then: [pause: short] + - T: "sobre" + - test: + if: "not(IsNode(*[2],'leaf'))" + then: [pause: short] + - x: "*[2]" + - pause: short + # ALINHADO ao en: o marcador de fim é dito apenas para cegueira + - test: + if: "$Impairment = 'Blindness'" + then: [T: "fim da fração"] + - pause: medium + +# ACRESCENTADO: leitura literal de msqrt/mroot sem semântica inferida. +# O inglês evita dizer "quadrada" porque a forma literal não garante índice. +- name: literal-default + tag: msqrt + match: "." + replace: + - test: + if: "$Verbosity!='Terse'" + then: [OT: "a"] + - T: "raiz" + - test: + if: "$Verbosity!='Terse'" + then: [T: "de"] + - x: "*[1]" + - pause: short + - test: + if: "not(IsNode(*[1],'leaf')) or $Impairment = 'Blindness'" + then: [T: "fim da raiz", pause: medium] + +- name: literal-default + # audit-ignore: divergência deliberada do en — ordem portuguesa: raiz de índice N de X — o teste final acompanha o último elemento falado + tag: mroot + match: "." + replace: + - test: + if: "$Verbosity!='Terse'" + then: [OT: "a"] + - T: "raiz de índice" + - x: "*[2]" + - pause: short + - T: "de" + - x: "*[1]" + - pause: short + - test: + if: "not(IsNode(*[1],'leaf'))" + then: [T: "fim da raiz"] + +- 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 + # invisible comma -- want "x 1 when subscript is an integer" + match: "count(*)=2 and (IsNode(*[2], 'leaf') or *[2][self::m:mrow][*[2][.='⁣']])" + replace: + - x: "*[1]" + - test: + if: "$Verbosity!='Terse' or not(*[2][self::m:mn])" # just say "x 1" for terse vs "x sub 1" + then: [T: "subscrito"] + - x: "*[2]" + +- name: literal + tag: msub + match: "." + replace: + - x: "*[1]" + - T: "subscrito" + - x: "*[2]" + +- name: literal + tag: [msup, msubsup] + match: "." + replace: + - x: "*[1]" + - test: + if: "name(.)='msubsup'" + then: + - T: "subscrito" + - 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: "sobrescrito" + - x: "*[last()]" + - test: + if: "not(IsNode(*[last()], 'simple'))" + then: [T: "fim do sobrescrito"] + else: + - T: "elevado à" + - x: "*[last()]" + - T: "potência" + +- name: default + tag: munder + match: "." + replace: + - test: + if: "not(IsNode(*[1], 'leaf'))" + then: [T: "modificado"] + - x: "*[1]" + - T: "com" + - x: "*[2]" + - T: abaixo +# NOTA: aqui existia uma regra "dot-over" que eu havia acrescentado supondo +# ser uma porta do inglês. Ela NÃO existe em en/ — o inglês trata a notação +# de Newton pela regra "diacriticals" abaixo, que apenas fala o caractere. +# A regra inventada encobria a diacriticals e produzia "x com ponto acima" +# no lugar de "x ponto". Removida. + +- name: diacriticals + tag: [mover, modified-variable] + match: "*[1][self::m:mi] and *[2][translate(., '\u0306\u030c.\u00A8\u02D9\u20DB\u20DC`^ˇ~→¯_', '')='']" + replace: + - x: "*[1]" + - x: "*[2]" + - pause: short + +- name: default + tag: mover + match: "." + replace: + - test: + if: "not(IsNode(*[1], 'leaf'))" + then: [T: "modificado"] + - x: "*[1]" + - T: "com" + - x: "*[2]" + - T: "acima de" + +- name: default + tag: munderover + match: "." + replace: + - test: + if: "not(IsNode(*[1], 'leaf'))" + then: [T: "modificado"] + - x: "*[1]" + - T: "com" + - x: "*[2]" + - T: "e abaixo" + - x: "*[3]" + - T: "acima de" + +- 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', 'pré-subscrito', 'pré-sub')" + - PreSuperscript: "IfThenElse($Verbosity='Verbose', 'pré-sobrescrito', 'pré-super')" + replace: + - test: # only bother announcing if there is more than one prescript + if: "count($Prescripts) > 2" + then: + - T: "com" + - x: "count($Prescripts) div 2" + - T: "pré-escritos" + - 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: "e"] + - 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: "e"] + - 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: "e pré-escritos alternados" + - x: "$Prescripts[position() > 4]" + - T: "fim dos pré-escritos" + - test: + if: "$Postscripts" + then: + - with: + variables: + - PostSubscript: "IfThenElse($Verbosity='Verbose', 'subscrito', 'sub')" + - PostSuperscript: "IfThenElse($Verbosity='Verbose', 'sobrescrito', 'super')" + replace: + - test: # only bother announcing if there is more than one postscript + if: "count($Postscripts) > 2" + then: + - test: + if: "$Prescripts" + then: [T: "e"] + - T: "com" + - x: "count($Postscripts) div 2" + - T: "pós-escritos" + - 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: "e"] + - 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: "e"] + - 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: "e"] + - 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: "e"] + - 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: "e índices alternados" + - x: "$Postscripts[position() > 8]" + - T: "fim dos índices" + + +- name: default + tag: [mtable, array] + variables: + - IsColumnSilent: "false()" + - NumColumns: "count(*[1]/*) - IfThenElse(*/self::m:mlabeledtr, 1, 0)" + match: "." + replace: + - T: "tabela com" + - x: count(*) + - test: + if: count(*)=1 + then: [T: "linha"] + else: [T: "linhas"] + - T: e + - x: "$NumColumns" + - test: + if: "$NumColumns" + then: [T: "coluna"] + else: [T: "colunas"] + - pause: long + - x: "*" + +- name: default + # ATUALIZADO para a geração atual do en: anuncia "linha N" e, no + # mlabeledtr, o rótulo — a versão herdada do es só falava os filhos, + # deixando tabelas genéricas sem anúncio de linha. + tag: [mtr, mlabeledtr] + match: "." + replace: + - pause: medium + - T: "linha" + - x: "count(preceding-sibling::*)+1" + - test: + if: "self::m:mlabeledtr" + then: + - T: "com rótulo" + - x: "*[1]/*" + - pause: short + - pause: medium + - test: + if: "self::m:mlabeledtr" + then: [x: "*[position()>1]"] + else: [x: "*"] + +- name: default + tag: mtd + match: "." + replace: + - T: "coluna" + - x: "count(preceding-sibling::*)+IfThenElse(parent::m:mlabeledtr, 0, 1)" + - pause: medium + - x: "*" # speak the entry + - 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: "célula vazia" + +- 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: "célula", pause: short] + - test: + if: ".[contains(@notation,'roundedbox')]" + then: [T: "célula redonda", pause: short] + - test: + if: ".[contains(@notation,'circle')]" + then: [T: "círculo", pause: short] + - test: + if: ".[ contains(concat(' ', normalize-space(@notation), ' '), ' left ') or contains(concat(' ', normalize-space(@notation), ' '), ' right ') or contains(@notation,'top') or contains(@notation,'bottom') ]" + then: + - T: linha acima + - test: + if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' left ')]" + then: [T: "esquerda", pause: short] + - test: + if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' right ')]" + then: [T: "direita", pause: short] + - test: + if: ".[contains(@notation,'top')]" + then: [T: "superior", pause: short] + - test: + if: ".[contains(@notation,'bottom')]" + then: [T: "inferior", pause: short] + - 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: "'x'", pause: short] # seems better to say 'x cross out' than 'up diagonal, down diagonal cross out' + else: + - test: + if: ".[contains(@notation,'updiagonalstrike')]" + then: [T: "diagonal superior", pause: short] + - test: + if: ".[contains(@notation,'downdiagonalstrike')]" + then: [T: "diagonal inferior", pause: short] + - test: + if: ".[contains(@notation,'verticalstrike')]" + then: [T: "vertical", pause: short] + - test: + if: ".[contains(@notation,'horizontalstrike')]" + then: [T: "horizontal", pause: short] + - T: riscado + - pause: short + - test: + if: ".[contains(@notation,'uparrow')]" + then: [T: "seta para cima", pause: short] + - test: + if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' downarrow ')]" + then: [T: "seta para baixo", pause: short] + - test: + if: ".[contains(@notation,'leftarrow')]" + then: [T: "seta para a esquerda", pause: short] + - test: + if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' rightarrow ')]" + then: [T: "seta para a direita", pause: short] + - test: + if: ".[contains(@notation,'northeastarrow')]" + then: [T: "seta para nordeste", pause: short] + - test: + if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' southeastarrow ')]" + then: [T: "seta para sudeste", pause: short] + - test: + if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' southwestarrow ')]" + then: [T: "seta para sudoeste", pause: short] + - test: + if: ".[contains(@notation,'northwestarrow')]" + then: [T: "seta para noroeste", pause: short] + - test: + if: ".[contains(@notation,'updownarrow')]" + then: [T: "seta vertical de duas pontas", pause: short] + - test: + if: ".[contains(@notation,'leftrightarrow')]" + then: [T: "seta horizontal de duas pontas", pause: short] + - test: + if: ".[contains(@notation,'northeastsouthwestarrow')]" + then: [T: "seta diagonal superior de duas pontas", pause: short] + - test: + if: ".[contains(@notation,'northwestsoutheastarrow')]" + then: [T: "seta diagonal inferior de duas pontas", pause: short] + - test: + if: ".[contains(@notation,'actuarial')]" + then: [T: "símbolo atuarial", pause: short] + - test: + if: ".[contains(@notation,'madrub')]" + then: [T: "símbolo fatorial arábico", pause: short] + - test: + if: ".[contains(@notation,'phasorangle')]" + then: [T: "ângulo fasorial", pause: short] + - test: + if: ".[contains(@notation,'longdiv') or not(@notation) or normalize-space(@notation) ='']" # default + then: [T: "símbolo de divisão longa", pause: short] + - test: + if: ".[contains(@notation,'radical')]" + then: [T: "raiz quadrada", pause: short] + - T: em torno de + - test: + if: "*[self::m:mtext and .=' ']" + then: [T: "espaço"] # otherwise there is complete silence + else: [x: "*"] + - test: + if: "$Impairment = 'Blindness' and ( $SpeechStyle != 'SimpleSpeak' or not(IsNode(*[1], 'leaf')) )" + then: [T: fim do destaque] + - 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: "de" + - pause: auto + - x: "*[position() > 1]" + +# Here are the intent hints that need to be handled: 'prefix' | 'infix' | 'postfix' | 'function' | 'silent' +- name: produto-vetorial-ou-escalar-sem-filhos + # O motor infere ⃗a × ⃗b e ⃗a ⋅ ⃗b como um elemento / + # SEM filhos (o próprio operador vira o elemento, data-from-mathml='mo'). + # Nenhuma regra de intent casa com count(*)=0, e a regra genérica falava o + # NOME do intent: "cross product", em inglês. Aqui passa pelo IntentMappings + # do pt. Ver ACHADOS 9.4. + tag: [cross-product, dot-product] + match: "count(*)=0" + replace: + - x: "SpeakIntentName(name(.), $Verbosity, 'infix')" + +- 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] + +# ACRESCENTADO: potência cujo expoente carrega um índice (x^(y_z)). +- name: power-indexed-by + tag: power-indexed-by + match: "." + replace: + - x: "*[1]" + - T: "sub" + - x: "*[2]" + - pause: short + +# ACRESCENTADO: intents com a propriedade :nofix: os argumentos já +# vêm na ordem de fala; basta falar o nome do intent. +- name: nofix-intent + 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 .... The MathML spec requires arguments to functions + tag: "*" + match: "count(*)>0" + replace: + - x: "SpeakIntentName(name(.), $Verbosity, 'function')" + - test: + if: "not(contains(@data-intent-property, ':literal:')) and + not(count(*)=2 and (IsInDefinition(*[1], 'TrigFunctionNames') or IsInDefinition(name(.), 'TerseFunctionNames')) and IsNode(*[2], 'simple'))" + then: [T: "de", pause: auto] # phrase(sine 'of' 5) + - insert: + nodes: "*" + replace: + - test: + if: "not(contains(@data-intent-property, ':literal:'))" + then: [x: "','"] + - pause: auto + - test: + if: "$Impairment = 'Blindness' and not(*[last()][IsBracketed(., '', '') or IsNode(., 'simple')] )" + then: [x: "GetBracketingIntentName(name(.), $Verbosity, 'function', 'end')"] + - test: + if: "IsNode(., '2D')" + then: [pause: short] + else: [pause: auto] + + +- name: default-text + # unknown leaf -- just speak the text -- could be a literal intent + tag: "*" + match: "." + replace: + - x: "translate(name(), '-_', ' ')" diff --git a/Rules/Languages/pt/SharedRules/general.yaml b/Rules/Languages/pt/SharedRules/general.yaml new file mode 100644 index 000000000..5b5a69634 --- /dev/null +++ b/Rules/Languages/pt/SharedRules/general.yaml @@ -0,0 +1,1121 @@ +--- + +# 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: "a" + - bookmark: "*[2]/@id" + - test: + - if: "*[2][.='+']" + then: [T: "positivo"] + else: [T: "negativo"] + - bookmark: "*[1]/@id" + - test: + - if: "*[1][.='ℂ']" + then: [T: números complexos] + - else_if: "*[1][.='ℕ']" + then: [T: números naturais] + - else_if: "*[1][.='ℚ']" + then: [T: números racionais] + - else_if: "*[1][.='ℝ']" + then: [T: números reais] + - else_if: "*[1][.='ℤ']" + then: [T: inteiros] + 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"] + - else_if: "*[1][.='ℕ']" + then: [T: "N"] + - else_if: "*[1][.='ℚ']" + then: [T: "Q"] + - else_if: "*[1][.='ℝ']" + then: [T: "R"] + - else_if: "*[1][.='ℤ']" + then: [T: "Z"] + 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: números complexos] + - else_if: ".='ℕ'" + then: [T: números naturais] + - else_if: ".='ℚ'" + then: [T: números racionais] + - else_if: ".='ℝ'" + then: [T: números reais] + - else_if: ".='ℤ'" + then: [T: inteiros] + else: [x: "text()"] # shouldn't happen + +- name: real-part + tag: real-part + match: "." + replace: + - bookmark: "@id" + - T: a parte real +- name: imaginary-part + tag: imaginary-part + match: "." + replace: + - bookmark: "@id" + - T: a parte imaginária# rules on scripted vertical bars ('evaluated at') +- name: evaluated-at-2 + tag: evaluate + match: "count(*)=2" + replace: + - x: "*[1]" + - pause: auto + - T: avaliado em + - pause: auto + - x: "*[2]" + +- name: evaluated-at-3 + tag: evaluate + match: "count(*)=3" + replace: + - x: "*[1]" + - pause: auto + - T: avaliado em + - pause: auto + - x: "*[3]" + - T: "avaliado na mesma expressão" + - x: "*[2]" + +- name: permutation + tag: pochhammer + match: "count(*)=2" + replace: + - x: "*[2]" + - T: permutações de + - x: "*[1]" + +- name: intervals + tag: [open-interval, open-closed-interval, closed-interval, closed-open-interval] + match: "count(*)=2" + replace: + - test: + if: "$Verbosity!='Terse'" + then: + - T: "o" + - test: + - if: "self::m:open-interval" + then: [T: "intervalo aberto"] + - else_if: "self::m:closed-interval" + then: [T: "intervalo fechado"] + - else_if: "self::m:open-closed-interval" + then: [T: "intervalo aberto fechado"] + else: [T: "intervalo fechado aberto"] + - test: + if: "$Verbosity!='Terse'" + then: + - T: "de" + - x: "*[1]" + - T: "a" + - x: "*[2]" + else: + - x: "*[1]" + - T: "vírgula" + - x: "*[2]" + +- name: default-point + tag: point + match: "count(*)=2" + replace: + - test: + if: "$Verbosity!='Terse'" + then: + - T: "o" + - T: "ponto" + - x: "*[1]" + - T: "vírgula" + - x: "*[2]" + +- name: bigop-both + tag: large-op + match: "count(*) = 3" + replace: + - test: + if: "$Verbosity!='Terse'" + then_test: + # "a integral", "o somatório": o artigo concorda com o operador. + if: "*[1][self::m:mo][contains('∫∬∭∮∯∰∱∲∳⨋⨌⨍⨎⨏⨐⨑⨒⨓⨔⨕⨖⨗⨘⨙⨚⨛⨜', .)]" + then: [T: a] + else: [T: o] + - x: "*[1]" + - T: de + - x: "*[2]" + - T: a + - x: "*[3]" + - test: + if: "following-sibling::*" + then: [T: de] + +- name: bigop-under + tag: large-op + match: "count(*)=2" + replace: + - test: + if: "$Verbosity!='Terse'" + then_test: + # "a integral", "o somatório": o artigo concorda com o operador. + if: "*[1][self::m:mo][contains('∫∬∭∮∯∰∱∲∳⨋⨌⨍⨎⨏⨐⨑⨒⨓⨔⨕⨖⨗⨘⨙⨚⨛⨜', .)]" + then: [T: a] + else: [T: o] + - x: "*[1]" + - T: sobre + - x: "*[2]" + - test: + if: "following-sibling::*" + then: [T: de] + +- name: largeop + tag: mrow + match: "count(*)=2 and IsInDefinition(*[1], 'LargeOperators')" + replace: + - test: + if: "$Verbosity!='Terse'" + then_test: + # "a integral", "o somatório": o artigo concorda com o operador. + if: "*[1][self::m:mo][contains('∫∬∭∮∯∰∱∲∳⨋⨌⨍⨎⨏⨐⨑⨒⨓⨔⨕⨖⨗⨘⨙⨚⨛⨜', .)]" + then: [T: a] + else: [T: o] + - x: "*[1]" + - T: de + - x: "*[2]" + +- name: msubsup-skip-super + # handles single, double, etc., prime + tag: [skip-super, say-super] + match: "count(*)=3" + replace: + - x: "*[1]" + # CORREÇÃO: a distinção Verbose/conciso do inglês ("subscript"/"sub") + # tinha colapsado, os dois ramos diziam a mesma coisa. Restaurada, + # coerente com as abreviações de default.yaml ('subscrito'/'sub'). + # Também restaurada a condição $Impairment='Blindness' do match interno, + # presente no inglês e perdida na tradução. + - test: + if: "$Verbosity='Verbose'" + then: [T: "subscrito"] + else: [T: "sub"] + - x: "*[2]" + - test: + if: "not(IsNode(*[2],'leaf') and $Impairment = 'Blindness')" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "fim do subscrito"] + else: [T: "fim do sub"] + - 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: "sobrescrito"] + else: [T: "super"] + - x: "*[3]" + - pause: short + +# 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" +# ACRESCENTADO (en): no modo conciso, unidade de uma letra é soletrada +# ("3 m" -> "3 m" soletrado), em vez de expandida para "metros". +- name: unit-terse + tag: unit + match: "$Verbosity = 'Terse' and string-length(.)=1" + replace: + - bookmark: "@id" + - spell: "text()" + +- 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 msup/mrow) + # But it might be in a fraction as (C) "3 m/s" (parent is mfrac/mrow) or (D) "3 m^2/s^2" (parent is msup/mfrac/mrow) + # or even (E) {3 m^2}/s (parent is msup/mrow) + # If in a fraction, only look in the numerator to find the coefficient + - 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()" + + - with: + variables: + - Word: "IfThenElse(substring($Word, string-length($Word)-2, 3) = 'ián', concat(substring($Word, 1, string-length($Word)-2), ian), $Word)" + - LastChar: "substring($Word, string-length($Word), 1)" + - PluralEnding: "IfThenElse($LastChar = 'r' or $LastChar = 'l', 'es', + IfThenElse(substring($Word, string-length($Word)-2, 3) = 'ian', 'es', 's' )) " + replace: + # 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: "concat($Word, $PluralEnding)"] + else: + - x: "$Prefix" + - 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, $PluralEnding)"] + + + +# ACRESCENTADO: valores monetários "R$ 100" -> "100 reais". +# Depende de CurrencySymbols e dos plurais em definitions.yaml. +- name: currency + tag: mrow + match: "count(*)=3 and DefinitionValue(*[1], 'Speech', 'CurrencySymbols') != ''" + variables: + # se o valor é 1, não pluraliza + - 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: "s"] + +- name: sin + tag: mi + match: ".='sin' or .='sen'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: sen] + else: [T: seno] +- name: cos + tag: mi + match: ".='cos'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: cos] + else: [T: cosseno] +- name: tan + tag: mi + match: ".='tan' or .='tg'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: tan] + else: [T: tangente] +- name: sec + tag: mi + match: ".='sec'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: sec] + else: [T: secante] +- name: csc + tag: mi + match: ".='csc'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + # CORREÇÃO: dizia "sec", idêntico à regra sec; cossecante e secante + # ficavam indistinguíveis na fala concisa. + then: [T: cossec] + else: [T: cossecante] +- name: cot + tag: mi + match: ".='cot'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: cotan] + else: [T: cotangente] + +- name: sinh + tag: mi + match: ".='sinh'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: seno hiperbólico] + else: [T: seno hiperbólico] +- name: cosh + tag: mi + match: ".='cosh'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: cosseno hiperbólico] + else: [T: cosseno hiperbólico] +- name: tanh + tag: mi + match: ".='tanh'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: tangente hiperbólica] + else: [T: tangente hiperbólica] +- name: sech + tag: mi + match: ".='sech'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: secante hiperbólica] + else: [T: secante hiperbólica] +- name: csch + tag: mi + match: ".='csch'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: cossecante hiperbólica] + else: [T: cossecante hiperbólica] +- name: coth + tag: mi + match: ".='coth'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + + then: [T: cotangente hiperbólica] + else: [T: cotangente hiperbólica] + +- name: exponential + tag: mi + match: ".='exp'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "exp"] + else: [T: "exponencial"] +- name: covariance + tag: mi + match: ".='Cov'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "Cov"] + else: [T: "covariância"] + +- + # CORREÇÃO: faltava o 'Log' maiúsculo (valor principal), presente no inglês. + # Sem ele, "Log x" caía na regra genérica de mi e saía "Log de x", perdendo a + # distinção. A tradução de "principal value log" ainda não foi verificada por + # falante nativo -- daí o 't:' minúsculo. + 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: "o"] # phrase('the' logarithm function is used in mathematics) + - test: + - if: ".= 'log'" + then: [T: "log"] # phrase(the 'log' function is used in mathematics) + - else_if: ".= 'Log'" + then: [t: "log do valor principal"] # (en: 'principal value log') + - else_if: "$Verbosity='Terse'" + then: [SPELL: "'ln'"] + else: [T: "logaritmo natural"] # phrase(the 'natural log' function is used in mathematics) + +- name: multi-line + # audit-ignore: divergência deliberada do en — plural por cadeias, não por concatenação de s + # 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: [IsColumnSilent: true()] + replace: + - x: "count(*)" + # CORREÇÃO: o plural era feito concatenando "s" (ct: "s"), o que produzia + # "equaçãos". Substituído por duas cadeias, singular e plural, escolhidas + # pela contagem — mesma correção já aplicada no ClearSpeak. + - test: + if: "count(*) > 1" + then: + - test: + - if: "self::m:piecewise" + then: [T: "casos"] + - else_if: "self::m:system-of-equations" + then: [T: "equações"] + else: [T: "linhas"] + else: + - test: + - if: "self::m:piecewise" + then: [T: "caso"] + - else_if: "self::m:system-of-equations" + then: [T: "equação"] + else: [T: "linha"] + - pause: short + - x: "*" + +- name: default-multiline + tag: [mtr, mlabeledtr] + match: "parent::m:piecewise or parent::m:system-of-equations or parent::m:lines" + replace: + - pause: medium + - test: + - if: "parent::m:piecewise" + then: [T: "caso"] + - else_if: "parent::m:system-of-equations" + then: [T: "equação"] + else: [T: "linha"] + - x: "count(preceding-sibling::*)+1" + - test: + if: .[self::m:mlabeledtr] + then: + - T: rotulada com + - x: "*[1]/*" + - 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: + - x: "*" + - test: + # short 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" + then_test: + if: "$LongPause" + then: [pause: medium] + else: [pause: short] + - else_if: "count(../following-sibling::*) > 0" + then_test: + if: "$LongPause" + then: [pause: long] + else: [pause: medium] + else: [pause: long] + +# 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 +# ACRESCENTADO: regra presente em en/ e ausente em es/ (e portanto em pt). +# Sem ela, dízimas periódicas (0,333...) não eram anunciadas como periódicas. +- name: repeating-decimal + tag: repeating-decimal + match: "." + replace: + - x: "*[1]" + - T: "com dígitos periódicos" + - spell: "*[2]" + +# NOTA: o inglês funde matrix e determinant numa regra só. Em português os +# artigos diferem ("a matriz" / "o determinante"), então a separação é +# deliberada, não é lacuna de paridade. +- name: 1x1-matrix + # audit-ignore: divergência deliberada do en — separada de 1x1-determinant: artigos de gêneros diferentes + tag: matrix + variables: [IsColumnSilent: true()] + match: "count(*)=1 and *[self::m:mtr][count(*) = 1]" + replace: + - OT: "a" + - T: "matriz 1 por 1" + - T: "com o elemento" + - x: "*[1]/*" + +- name: 1x1-determinant + # audit-ignore: divergência deliberada do en — idem + tag: determinant + variables: [IsColumnSilent: true()] + match: "count(*)=1 and *[self::m:mtr][count(*) = 1]" + replace: + - OT: "o" + - T: "determinante 1 por 1" + - T: "com o elemento" + - x: "*[1]/*" + + +- name: zero-matrix + tag: matrix + match: "not( */*/*[not(self::m:mn and .= 0)] )" + replace: + - T: "a matriz nula" + - x: count(*) + - T: "por" + - x: count(*[self::m:mtr][1]/*) + - pause: long + +- name: identity-matrix + tag: matrix + match: + - "count(*) = count(*[1]/*) and " + - "not( */*[count(preceding-sibling::*) = count(../preceding-sibling::*)]/*[not(self::m:mn and .= 1)] ) and " + - "not( */*[count(preceding-sibling::*) != count(../preceding-sibling::*)]/*[not(self::m:mn and .= 0)] )" + replace: + - T: "a matriz identidade" + - x: count(*) + - T: "por" + - x: count(*[self::m:mtr][1]/*) + - pause: long + +- name: diagonal-matrix + tag: matrix + match: + - "count(*) = count(*[1]/*) and " + - "not( */*/*[not(self::m:mn and .= 0)]" + - " [count(../preceding-sibling::*)!=count(../../preceding-sibling::*)]" + - " )" + replace: + - T: "a matriz diagonal" + - x: count(*) + - T: "por" + - x: count(*[self::m:mtr][1]/*) + - pause: long + - insert: + 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: + - test: + if: "$ClearSpeak_Matrix = 'Vector' or $ClearSpeak_Matrix = 'EndVector'" + then: [T: "o vetor coluna"] + else: [T: "a matriz coluna"] + - x: count(*) + - T: "por 1 coluna" + - pause: long + - x: "*/*" + - test: + if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" + then: + - T: "fim" + - test: + if: $ClearSpeak_Matrix = 'EndVector' + then: [T: vetor] + else: [T: matriz] + +- name: default-column-matrix + tag: matrix + variables: [IsColumnSilent: true()] + match: "*[self::m:mtr][count(*) = 1]" + replace: + - test: + if: "$ClearSpeak_Matrix = 'Vector' or $ClearSpeak_Matrix = 'EndVector'" + then: [T: "o vetor"] + else: [T: "a matriz"] + - x: "count(*)" + - T: "por 1 coluna" + - pause: long + - x: "*" # select the rows (mtr) + - test: + if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" + then: [T: "fim da matriz"] + +- name: 1x2-or-3-matrix + tag: matrix + variables: [IsColumnSilent: "$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: + - test: + if: "$ClearSpeak_Matrix = 'Vector' or $ClearSpeak_Matrix = 'EndVector'" + then: [T: "o vetor"] + else: [T: "a matriz"] + - T: "1 por" + - x: count(*/*) + - T: "linha" + - pause: long + - x: "*/*" + - test: + if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" + then: + - T: "fim" + - test: + if: $ClearSpeak_Matrix = 'EndMatrix' + then: [T: matriz] + else: [T: vetor] + +- name: default-row-matrix + tag: matrix + variables: [IsColumnSilent: "$SpeechStyle = 'ClearSpeak' and $ClearSpeak_Matrix = 'SilentColNum'"] + match: "count(*)=1" # one row + replace: + - test: + if: "$ClearSpeak_Matrix = 'Vector' or $ClearSpeak_Matrix = 'EndVector'" + then: [T: "o vetor"] + else: [T: "a matriz"] + - T: "1 por" + - x: "count(*/*)" + - T: "linha" + - pause: long + - x: "*/*" # select the cols (mtd) + - test: + if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" + then: + - T: "fim" + - test: + if: $ClearSpeak_Matrix = 'EndMatrix' + then: [T: matriz] + else: [T: vetor] + +- 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 = 'ClearSpeak' and $ClearSpeak_Matrix != 'SpeakColNum'"] + replace: + - test: + if: "self::m:determinant" + then: [T: "o determinante"] + else: [T: "a matriz"] + - x: count(*) + - T: por + - x: count(*[self::m:mtr][1]/*) + - pause: long + - x: "*" + - test: + if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" + then: + - T: fim + - test: + if: "self::m:determinant" + then: [T: "determinante"] + else: [T: "matriz"] + +- name: default-matrix + tag: [matrix, determinant] + variables: [IsColumnSilent: "$SpeechStyle = 'ClearSpeak' and $ClearSpeak_Matrix = 'SilentColNum'"] + match: "not(@data-intent-property)" + replace: + - test: + if: "self::m:determinant" + then: [T: "o determinante"] + else: [T: "a matriz"] + - x: "count(*)" + - T: por + - x: "count(*[self::m:mtr][1]/*)" + - pause: long + - x: "*" + - test: + if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" + then: + - T: fim + - test: + if: "self::m:determinant" + then: [T: determinante] + else: [T: matriz] + +- name: chemistry-msub + + tag: [chemical-formula] + match: "*[1][.='msub']" + replace: + - x: "*[2]" + - test: + if: "$Verbosity='Verbose'" + then: [T: "subscrito"] + else_test: + if: "$Verbosity='Medium'" + then: [T: "sub"] + - x: "*[3]" + +# ACRESCENTADO: dimensões de matriz anotadas como dimension-product +# ("3×5" com intent de dimensão) fala "3 por 5". +- name: dimension-by + tag: mrow + match: dimension-product + replace: + - insert: + nodes: "*" + replace: [T: "por", pause: auto] + +- name: chemistry-msup + tag: [chemical-formula] + match: "count(*)=3 and *[1][.='msup']" + replace: + - x: "*[2]" + - test: + if: "$Verbosity='Verbose'" + then: [T: "sobrescrito"] + else_test: + if: "$Verbosity='Medium'" + then: [T: "super"] + - 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: "sobrescrito"] + else_test: + if: "$Verbosity='Medium'" + then: [T: "super"] + - x: "$Prescripts[2]" + - pause: "short" + - test: + if: "not($Prescripts[1][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "subscrito"] + else_test: + if: "$Verbosity='Medium'" + then: [T: "sub"] + - 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: "sobrescrito"] + else_test: + if: "$Verbosity='Medium'" + then: [T: "super"] + - x: "$Prescripts[4]" + - pause: "short" + - test: + if: "not($Prescripts[3][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "subscrito"] + else_test: + if: "$Verbosity='Medium'" + then: [T: "sub"] + - 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: "subscrito"] + else_test: + if: "$Verbosity='Medium'" + then: [T: "sub"] + - x: "$Postscripts[1]" + - pause: "short" + - test: + if: "not($Postscripts[2][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "sobrescrito"] + else_test: + if: "$Verbosity='Medium'" + then: [T: "super"] + - 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: "subscrito"] + else_test: + if: "$Verbosity='Medium'" + then: [T: "sub"] + - x: "$Postscripts[3]" + - pause: "short" + - test: + if: "not($Postscripts[4][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "sobrescrito"] + else_test: + if: "$Verbosity='Medium'" + then: [T: "super"] + - 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: "subscrito"] + else_test: + if: "$Verbosity='Medium'" + then: [T: "sub"] + - x: "$Postscripts[5]" + - pause: "short" + - test: + if: "not($Postscripts[6][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "sobrescrito"] + else_test: + if: "$Verbosity='Medium'" + then: [T: "super"] + - 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: "subscrito"] + else_test: + if: "$Verbosity='Medium'" + then: [T: "sub"] + - x: "$Postscripts[7]" + - pause: "short" + - test: + if: "not($Postscripts[8][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "sobrescrito"] + else_test: + if: "$Verbosity='Medium'" + then: [T: "super"] + - 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: "sólido"] + - else_if: ".='l'" + then: [T: "líquido"] + - else_if: ".='g'" + then: [T: "gasoso"] + else: [T: "aquoso"] # (aq) + - 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: "ligação simples"] + - else_if: ".='=' or .='∷'" # U+2237: o canonicalizador converte '::' nesse caractere + then: [T: "ligação dupla"] + - else_if: ".='≡'" + then: [T: "ligação tripla"] + - else_if: ".='≣'" + then: [T: "ligação quádrupla"] + 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: "forma"] + else: [T: "reage formando"] + - else_if: ".='⇌' or .='🣑'" # U+01F8D1 + then: [T: "está em equilíbrio com"] + - else_if: ".='🣓'" # U+1F8D3 + then: [T: "está em equilíbrio deslocado para a esquerda com"] + - else_if: ".='🣒'" # U+1F8D2 + then: [T: "está em equilíbrio deslocado para a direita com"] + 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 + +# ACRESCENTADO: fora da química, cai na regra 'default-text', que fala o +# nome da tag -- em inglês isso dá "none", que é aceitável lá, mas em português +# saía a palavra inglesa. Aqui traduzimos. +- name: none-default + tag: none + match: "." + replace: + - T: "nenhum" + +- name: ignore-intent-wrapper + tag: intent-wrapper + match: "." + replace: + - x: "*" diff --git a/Rules/Languages/pt/SharedRules/geometry.yaml b/Rules/Languages/pt/SharedRules/geometry.yaml new file mode 100644 index 000000000..24227e81b --- /dev/null +++ b/Rules/Languages/pt/SharedRules/geometry.yaml @@ -0,0 +1,80 @@ + +--- + +- name: line-segment + tag: line-segment + match: "count(*)=2" + replace: + - test: + if: "$Verbosity='Verbose'" + then: + - T: "o segmento de reta de" + - x: "*[1]" + - T: "a" + - x: "*[2]" + else: + - T: "segmento de reta" + - x: "*[1]" + - x: "*[2]" + +- name: geometry-ray + tag: ray + match: "count(*)=2" + replace: + - test: + if: "$Verbosity='Verbose'" + then: + - T: "a semirreta de" + - x: "*[1]" + - T: "a" + - x: "*[2]" + else: + - T: "semirreta" + - x: "*[1]" + - x: "*[2]" + +- name: geometry-arc + tag: arc + match: "count(*)=2" + replace: + - test: + if: "$Verbosity='Verbose'" + then: [T: "o"] + - T: "arco" + - x: "*[1]" + - x: "*[2]" + +- name: measure-of-angle + tag: measure-of-angle + match: "count(*)=3" + replace: + - test: + if: "$Verbosity='Verbose'" + then: + - T: "a medida do ângulo" + else: + - T: "medida do ângulo" + - x: "*[1]" + - x: "*[2]" + - x: "*[3]" + +# ACRESCENTADO +- name: coordinate + tag: coordinate + match: "." + replace: + - test: + if: "$Verbosity='Verbose'" + then: [OT: "o"] + - T: "ponto" + - test: + if: "$Verbosity='Verbose'" + then: [T: "em"] + - pause: short + - insert: + nodes: "*" + replace: [T: "vírgula", pause: auto] + - pause: short + - test: + if: "($SpeechStyle='ClearSpeak' and $Verbosity='Verbose') or not(IsNode(*[last()],'leaf'))" + then: [T: "fim do ponto"] diff --git a/Rules/Languages/pt/SharedRules/linear-algebra.yaml b/Rules/Languages/pt/SharedRules/linear-algebra.yaml new file mode 100644 index 000000000..72acf26be --- /dev/null +++ b/Rules/Languages/pt/SharedRules/linear-algebra.yaml @@ -0,0 +1,35 @@ +--- + +- name: scalar-determinant + tag: determinant + match: "count(*)=1 and not(*[1][self::m:mtr])" + replace: + - test: + if: "$Verbosity='Verbose'" + then: + - T: "o" + - T: "determinante" + - test: + if: "$Verbosity!='Terse'" + then: + - T: "de" + - x: "*[1]" + - test: + if: "not(IsNode(*[1], 'simple'))" + then: [T: "fim do determinante"] + +- name: subscripted-norm + tag: subscripted-norm + match: "count(*)=2" + replace: + - test: + if: "$Verbosity='Verbose'" + then: + - T: "a" + - x: "*[2]" + - T: "norma" + - test: + if: "$Verbosity!='Terse'" + then: + - T: "de" + - x: "*[1]" diff --git a/Rules/Languages/pt/SimpleSpeak_Rules.yaml b/Rules/Languages/pt/SimpleSpeak_Rules.yaml new file mode 100644 index 000000000..7677751f0 --- /dev/null +++ b/Rules/Languages/pt/SimpleSpeak_Rules.yaml @@ -0,0 +1,338 @@ +--- + +- 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: "menos" # phrase(x 'minus' y) + - x: "translate(text(), '-_', '')" + +- name: default + tag: square-root + match: "." + replace: + - test: + if: "$Verbosity!='Terse'" + then: [T: "a"] # phrase('the' square root of x) — "a raiz" (feminino) + - T: "raiz quadrada" # phrase(the 'square root' of x) + - test: + if: "$Verbosity!='Terse'" + then: [T: "de"] # phrase(the square root 'of' x) + else: [pause: short] + - x: "*[1]" + - test: + if: IsNode(*[1], 'leaf') + then: [pause: short] + else: [T: "fim da raiz", pause: short] # phrase(the square root of x 'end root') + +- name: default + tag: root + match: "." + replace: + - test: + if: "$Verbosity!='Terse'" + then: [T: a] + - test: + if: "*[2][self::m:mn]" + then_test: + - if: "*[2][.='2']" + then: [T: "raiz quadrada"] + - else_if: "*[2][.='3']" + then: [T: "raiz cúbica"] + - else_if: "*[2][not(contains(., '.'))]" + then: [T: "raiz de índice", x: "*[2]"] + else: + # CORREÇÃO: o arquivo em espanhol manteve o bloco "pronounce" com o + # sufixo inglês "-th" , herdado do inglês "n-th root". + # Em português a leitura natural é "raiz de índice n", sem sufixo... + - T: "raiz de índice" + - x: "*[2]" + - test: + if: "$Verbosity!='Terse'" + then: [T: "de"] + - x: "*[1]" + - test: + if: IsNode(*[1], 'leaf') + then: [pause: short] + else: [T: "fim da raiz", pause: short] + +# 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]" + replace: [x: ToCommonFraction(.)] +- 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(., '.'))]" + replace: [x: ToCommonFraction(.)] + + +# 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: "por" # phrase('5 meters 'per' second) — "cinco metros por segundo" + - 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: "*[1]" + - T: "sobre" + - x: "*[2]" + - pause: short + +- name: default + tag: fraction + match: "." + replace: + - T: fração + - pause: short + - x: "*[1]" + - test: + if: "not(IsNode(*[1],'leaf'))" + then: [pause: short] + - T: "sobre" + - test: + if: "not(IsNode(*[2],'leaf'))" + then: [pause: short] + - x: "*[2]" + - pause: short + - T: "fim da fração" + - 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: + - T: "inversa de" # phrase(the 'inverse' of f) + - x: "*[1]" + +- 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_test: + if: "$Verbosity='Verbose'" + then: [T: "elevado ao quadrado"] + else: [T: "ao quadrado"] + # CORREÇÃO: o espanhol dizia "cúbica" (adjetivo feminino solto), + # incoerente com "al cuadrado" logo acima. O inglês é "cubed". + else: [T: "ao cubo"] +- name: function-power + tag: power + match: + - "following-sibling::*[1][self::m:mo][.='⁡']" #invisible function apply + replace: + - test: + if: "$Verbosity!='Terse'" + then: [T: a] + - bookmark: "*[2]/@id" + - T: "potência" + - x: "*[2]" + - T: "de" + - pause: short + - x: "*[1]" + +# 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_test: + if: "$Verbosity='Verbose'" + then: [T: "elevado ao quadrado"] + else: [T: "ao quadrado"] + else: [T: "ao cubo"] + +- name: simple-integer + tag: power + match: "*[2][self::m:mn][not(contains(., '.'))]" + replace: + - x: "*[1]" + - T: "elevado a" + - x: "*[2]" +- name: simple-negative-integer + tag: power + match: + - "*[2][self::m:minus and count(*)=1 and" + - " *[1][self::m:mn][not(contains(., '.'))]]" + replace: + - x: "*[1]" + - T: "elevado a" + - x: "*[2]" +- name: simple-var + tag: power + match: "*[2][self::m:mi][string-length(.)=1]" + replace: + - x: "*[1]" + - T: "elevado a" + - x: "*[2]" + +- name: simple + tag: power + match: "IsNode(*[2], 'leaf')" + replace: + - x: "*[1]" + - T: "elevado a" + - x: "*[2]" + +- 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: "elevado a" + - x: "*[2]" + - pause: short + # CORREÇÃO: o espanhol escreveu "fin de la exponente" — "exponente" é + # masculino, então já estava errado em espanhol ("fin del exponente"). + - T: "fim do expoente" +- name: default + tag: power + match: "." + replace: + - x: "*[1]" + # CORREÇÃO: a ordem herdada do inglês ("to the n plus 1 power") produzia + # "elevado à n mais 1 potência". Em português o núcleo vem antes do + # complemento: "elevado à potência n mais 1". + - T: "elevado à potência" + - x: "*[2]" + +# +# Some rules on mrows +# +- name: set + tag: set + match: "." + replace: + - test: + - if: "count(*)=0" + then: + - test: + if: "$Verbosity!='Terse'" + then: [T: o] + - T: "conjunto vazio" + - else_if: "count(*[1]/*)=3 and *[1]/*[2][self::m:mo][.=':' or .='|' or .='∣']" + then: + - test: + if: "$Verbosity!='Terse'" + then: [T: o] + - T: "conjunto de todos os" + - x: "*[1]/*[1]" + - T: "tal que" + - x: "*[1]/*[3]" + else: + - test: + if: "$Verbosity!='Terse'" + then: [T: o] + - T: "conjunto" + - x: "*[1]" + +- name: times + tag: mo + match: + # 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: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: + - test: + if: "$Verbosity = 'Verbose'" + then: [T: "multiplicado por"] # (en: 'times') + else: [T: "vezes"] + +- 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') )" + # 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/pt/definitions.yaml b/Rules/Languages/pt/definitions.yaml new file mode 100644 index 000000000..adf95240a --- /dev/null +++ b/Rules/Languages/pt/definitions.yaml @@ -0,0 +1,496 @@ +--- +- include: "../../definitions.yaml" +# Lines starting with "#" are a comment +# Each definition in this file is of the form +# - name: { "...", "..." "..." } +# For numbers, +# - name: [] "...", "..." "..." ] + + +# If an "intent" is used, the 'terse:medium:verbose' speech for the intent name is given here for a prefix||infix||postfix||function fixity +# If only one ":" is used, it the first part is used for 'terse' and the second part is used for 'medium' and 'verbose' +# If no ":"s are used, the same speech is used for all forms +# If bracketing words make sense, they are separated with ";"s +# Intent mappings must specify whether they are "prefix", "infix", "postfix", or "function" with an "=" sign +# If there are multiple fixities (e.g., see transpose), they are separated with "|| +# for readability, spaces can be used around any of the delimiter characters +# Note: if there are multiple fixities, the first one is used if the fixity is not given in the intent +- IntentMappings: { + "indexed-by": "infix= ; índice; fim do índice", + "modified-variable": "silent= ", + "say-super": "infix=sobrescrito", # used with 'mo' for superscripts (e.g, "<") + "skip-super": "silent=", # used with 'mo' for superscripts (e.g, "*") + + "absolute-value": "function= ; valor absoluto: o valor absoluto de: o valor absoluto de; fim do valor absoluto", + "binomial": "infix=binomial; escolhe; fim do binomial", + "limit": "prefix=limite quando", + "lim-sup": "prefix=lim sup quando: o limite superior quando: o limite superior quando", + "lim-inf": "prefix=lim inf quando: o limite inferior quando: o limite inferior quando", + "logarithm-with-base": "prefix=log na base: o log na base: o logaritmo na base", + "minus": "infix=menos || prefix=menos", + "plus": "infix=mais || prefix=positivo", + + "transpose": "postfix=transposta || function=transposta", + "norm": "function=; norma: norma: a norma; fim da norma", + # ‖x‖ é inferido pelo motor como 'magnitude' (não 'norm'); sem esta linha + # vazava "magnitude de x" em inglês. Mesma fala da norma. + "magnitude": "function=; norma: norma: a norma; fim da norma", + "trace": "function=; traço: traço de: o traço de; fim do traço", + "dimension": "function=; dimensão: dimensão de: a dimensão de; fim da dimensão", + "homomorphism": "function= ; homomorfismo: homomorfismo de: o homomorfismo de; fim do homomorfismo", + "kernel": "function= ; núcleo: núcleo de: o núcleo de; fim do núcleo", + "vector": "function= ; vetor || prefix=vetor", + # O motor infere ⃗a × ⃗b como um SEM filhos (o operador vira + # o próprio elemento); aí a fixidade infix não se aplica e ele falava o nome + # literal, "cross product". A fixidade nofix cobre esse caso. Idem dot-product. + "cross-product": "infix=vetorial: produto vetorial: produto vetorial || nofix=vetorial: produto vetorial: produto vetorial", + "dot-product": "infix=escalar: produto escalar: produto escalar || nofix=escalar: produto escalar: produto escalar", + + "divergence": "function= ; div: divergente: divergente; fim do divergente", + "curl": "function= ; rotacional; fim do rotacional", + "gradient": "function= ; grad: gradiente: gradiente; fim do gradiente", + "laplacian": "function=laplaciano", + + "chemistry-concentration": "function= ; concentração: concentração de: a concentração de; fim da concentração", + # CORREÇÃO: o en tem duas fixidades (nofix || function); só com a function + # a fala ganha o "de" ("probabilidade de x"). E o separador de palavras do + # conditional-probability é "|", como no en — ";" não é reconhecido aqui. + "probability": "nofix=P: probabilidade || function=probabilidade", + "conditional-probability": "function=probabilidade | dado", + "intersection": "infix=interseção", + "union": "infix=união", + } + + +# ACRESCENTADO: definição presente em en/ e ausente em es/ (e portanto em pt). +# ATENÇÃO: estes NÃO são textos falados — são nomes de intent (chaves internas +# do motor, sempre em inglês). A regra os consulta com IsInDefinition(name(.), ...). +# Traduzi-los quebraria o casamento. Copiados tal e qual do inglês. +- TerseFunctionNames: { + "divergence", "del:gradient", "curl" + } + +- KnownWords: { + "abs", "adj", "ano", "cel", "dez", "din", "elo", "fim", "lux", "nos" + } + +- NavigationParts: { + "large-op": "base; limite inferior; limite superior", + "mfrac": "numerador;denominador", + "fraction": "numerador;denominador", + "msqrt": "radicando", + "square-root": "radicando", + "mroot": "radicando;índice da raiz", + "root": "radicando;índice da raiz", + "msub": "base;subscrito", + "sub": "base;subscrito", + "msup": "base;sobrescrito", + "say-super": "base;sobrescrito", + "skip-super": "base;sobrescrito", + "power": "base;expoente", + "msubsup": "base;subscrito;sobrescrito", + "munder": "base;limite inferior", + "mover": "base;limite superior", + "munderover": "base;limite inferior;limite superior", + + # 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": "em", + "out": "saindo de", + } + +- SIPrefixes: { + "Q": "quetta", "R": "ronna", "Y": "yotta", "Z": "zetta", "E": "exa", "P": "peta", "T": "tera", "G": "giga", "M": "mega", "k": "quilo", "h": "hecto", "da": "deca", + "d": "deci", "c": "centi", "m": "mili", "µ": "micro", "n": "nano", "p": "pico", "f": "femto", "a": "atto", "z": "zepto", "y": "yocto", "r": "ronto", "q": "quecto" + } + +# this is a list of all units that accept SIPrefixes +# from www.bipm.org/documents/20126/41483022/SIBrochure-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": "ampère", + "cd": "candela", + "K": "kelvin", "K": "kelvin", + "g": "grama", + "m": "metro", + "mol": "mol", + "s": "segundo", "sec": "segundo", # "sec" not actually legal + + # derived units + "Bq": "becquerel", + "C": "coulomb", + "°C": "grau Celsius", "℃": "grau Celsius", # should only take negative powers + "F": "farad", + "Gy": "gray", + "H": "henry", + "Hz": "hertz", + "J": "joule", + "kat": "katal", + "lm": "lúmen", + "lx": "lux", + "N": "newton", + "Ω": "ohm", "Ω": "ohm", # Greek Cap letter, U+2126 OHM SIGN + "Pa": "pascal", + "S": "siemens", + "Sv": "sievert", + "T": "tesla", + "V": "volt", + "W": "watt", + "Wb": "weber", + + # accepted (plus a few variants) that take SI prefixes + "l": "litro", "L": "litro", "ℓ": "litro", # Should only take negative powers + "t": "tonelada", # should only take positive powers + "Da": "dalton", + "Np": "neper", # should only take negative powers + "u": "unidade de massa atômica", + "eV": "elétron-volt", + "rad": "radiano", # should only take negative powers + "sr": "esferorradiano", # should only take negative powers + + # others that take a prefix + "a": "ano", # should only take positive powers + "as": "segundo de arco", # see en.wikipedia.org/wiki/Minute_and_second_of_arc + + # technically wrong, but used in practice with SI Units (and only positive powers) + "b": "bit", # should only take positive powers + "B": "byte", # should only take positive powers + "Bd": "baud", # should only take positive powers + } + + +- UnitsWithoutPrefixes: { + # time + "″": "segundo", "\"": "segundo", + "′": "minuto", "'": "minuto","min": "minuto", + "h": "hora", "hr": "hora", "Hr": "hora", + "d": "dia", "dy": "dia", + "w": "semana", "wk": "semana", + "y": "ano", "yr": "ano", + + # angles + "°": "grau", "deg": "grau", # should only take negative powers + "arcmin": "minuto de arco", + "amin": "minuto de arco", + "am": "minuto de arco", + "MOA": "minuto de arco", + "arcsec": "segundo de arco", + "asec": "segundo de arco", + + # distance + "au": "unidade astronômica", "AU": "unidade astronômica", + "ltyr": "ano-luz", "ly": "ano-luz", + "pc": "parsec", + "Å": "angström", "Å": "angström", # U+00C5 and U+212B + "fm": "fermi", + + # others + "ha": "hectare", + # "B": "bel", # "B" more commonly means bytes + "dB": "decibel", # already logarithmic, so not used with SI prefixes + # radiano com prefixo dobra o r (nano+radiano = nanorradiano) + "mrad": "milirradiano", "µrad": "microrradiano", "nrad": "nanorradiano", "prad": "picorradiano", "krad": "quilorradiano", + + + # CORREÇÃO ORTOGRÁFICA pt: prefixo terminado em vogal seguido de + # palavra iniciada por "s" dobra o s ex: milissegundo, microssegundo, + # nanossegundo. É o mesmo caso já tratado para "radiano" acima + # (milirradiano). A regra genérica de composição do MathCAT concatena + # sem dobrar e produziria "milisegundo". + # "as" fica de fora: o símbolo já significa "segundo de arco". + "Qs": "quettassegundo", "Rs": "ronnassegundo", "Ys": "yottassegundo", "Zs": "zettassegundo", "Es": "exassegundo", + "Ps": "petassegundo", "Ts": "terassegundo", "Gs": "gigassegundo", "Ms": "megassegundo", "ks": "quilossegundo", + "hs": "hectossegundo", "das": "decassegundo", "ds": "decissegundo", "cs": "centissegundo", "ms": "milissegundo", + "µs": "microssegundo", "ns": "nanossegundo", "ps": "picossegundo", "fs": "femtossegundo", "zs": "zeptossegundo", + "ys": "yoctossegundo", "rs": "rontossegundo", "qs": "quectossegundo", + + "Qsec": "quettassegundo", "Rsec": "ronnassegundo", "Ysec": "yottassegundo", "Zsec": "zettassegundo", + "Esec": "exassegundo", "Psec": "petassegundo", "Tsec": "terassegundo", "Gsec": "gigassegundo", + "Msec": "megassegundo", "ksec": "quilossegundo", "hsec": "hectossegundo", "dasec": "decassegundo", + "dsec": "decissegundo", "csec": "centissegundo", "msec": "milissegundo", "µsec": "microssegundo", + "μsec": "microssegundo", "nsec": "nanossegundo", "psec": "picossegundo", "fsec": "femtossegundo", + "zsec": "zeptossegundo", "ysec": "yoctossegundo", "rsec": "rontossegundo", "qsec": "quectossegundo", + + + "μs": "microssegundo", + + + "cas": "centissegundo de arco", "mas": "milissegundo de arco", + "µas": "microssegundo de arco", "μas": "microssegundo de arco", "nas": "nanossegundo de arco", + "pas": "picossegundo de arco", "fas": "femtossegundo de arco", "zas": "zeptossegundo de arco", + "yas": "yoctossegundo de arco", "ras": "rontossegundo de arco", "qas": "quectossegundo de arco", + + "QS": "quettassiemens", "RS": "ronnassiemens", "YS": "yottassiemens", "ZS": "zettassiemens", "ES": "exassiemens", + "PS": "petassiemens", "TS": "terassiemens", "GS": "gigassiemens", "MS": "megassiemens", "kS": "quilossiemens", + "hS": "hectossiemens", "daS": "decassiemens", "dS": "decissiemens", "cS": "centissiemens", "mS": "milissiemens", + "µS": "microssiemens", "nS": "nanossiemens", "pS": "picossiemens", "fS": "femtossiemens", "aS": "attossiemens", + "zS": "zeptossiemens", "yS": "yoctossiemens", "rS": "rontossiemens", "qS": "quectossiemens", + + "QSv": "quettassievert", "RSv": "ronnassievert", "YSv": "yottassievert", "ZSv": "zettassievert", "ESv": "exassievert", + "PSv": "petassievert", "TSv": "terassievert", "GSv": "gigassievert", "MSv": "megassievert", "kSv": "quilossievert", + "hSv": "hectossievert", "daSv": "decassievert", "dSv": "decissievert", "cSv": "centissievert", "mSv": "milissievert", + "µSv": "microssievert", "nSv": "nanossievert", "pSv": "picossievert", "fSv": "femtossievert", "aSv": "attossievert", + "zSv": "zeptossievert", "ySv": "yoctossievert", "rSv": "rontossievert", "qSv": "quectossievert", + + "atm": "atmosfera", + "amu": "unidade de massa atômica", + "bar": "bar", + "cal": "caloria", + "Ci": "curie", + "grad": "grado", + "M": "molar", + "R": "roentgen", + "rpm": "rotação por minuto", + "℧": "mho", + "dyn": "dina", + "erg": "erg", + + # special case overrides because prefixes with the base "metro" all get accents + "Qm": "quetâmetro", "Rm": "ronâmetro", "Ym": "yotâmetro", "Zm": "zetâmetro", "Em": "exâmetro", "Pm": "petâmetro", + "Tm": "terâmetro", "Gm": "gigâmetro", "Mm": "megâmetro", "km": "quilômetro", "hm": "hectômetro", "dam": "decâmetro", + "dm": "decímetro", "cm": "centímetro", "mm": "milímetro", "µm": "micrômetro", "nm": "nanômetro", "pm": "picômetro", + # conflicts: "fm": "femtômetro", "am": "attômetro", + "zm": "zeptômetro", "ym": "yoctômetro", "rm": "rontômetro", "qm": "quectômetro", + + # powers of 2 used with bits and bytes + "Kib": "kibibit", "Mib": "mebibit", "Gib": "gibibit", "Tib": "tebibit", "Pib": "pebibit", "Eib": "exbibit", "Zib": "zebibit", "Yib": "yobibit", + "KiB": "kibibyte", "MiB": "mebibyte", "GiB": "gibibyte", "TiB": "tebibyte", "PiB": "pebibyte", "EiB": "exbibyte", "ZiB": "zebibyte", "YiB": "yobibyte", + } + + + # this will only be used if the language is English, so it can be empty for other countries + # Mantida em inglês de propósito: são unidades imperiais, sem uso no Brasil. +- EnglishUnits: { + # length + "in": "inch", + "ft": "foot", + "mi": "mile", + "rd": "rod", + "li": "link", + "ch": "chain", + + # area + "sq in": "square inch", "sq. in": "square inch", "sq. in.": "square inch", + "sq ft": "square foot", "sq. ft": "square foot", "sq. ft.": "square foot", + "sq yd": "square yard", "sq. yd": "square yard", "sq. yd.": "square yard", + "sq mi": "square mile", "sq. mi": "square mile", "sq. mi.": "square mile", + "ac": "acre", + "FBM": "board foot", + + # volume + "cu in": "cubic inch", "cu. in": "cubic inch", "cu. in.": "cubic inch", + "cu ft": "cubic foot", "cu. ft": "cubic foot", "cu. ft.": "cubic foot", + "cu yd": "cubic yard", "cu. yd": "cubic yard", "cu. yd.": "cubic yard", + "bbl": "barrel", "BBL": "barrel", + "pk": "peck", + "bu": "bushel", + "tsp": "teaspoon", + "tbl": "tablespoon", + + # liquid + "fl dr": "fluid drams", + "fl oz": "fluid ounce", + "gi": "gill", + "cp": "cup", "cup": "cup", + "pt": "pint", + "qt": "quart", + "gal": "gallon", + + # weight + "gr": "grain", + "dr": "dram", + "oz": "ounce", "℥": "ounce", + "lb": "pound", + "cwt": "hundredweight", + "dwt": "pennyweight", + "oz t": "troy ounce", + "lb t": "troy pound", + + # energy + "hp": "horsepower", + "BTU": "BTU", + "°F": "degree fahrenheit", "℉": "degree fahrenheit", + + # other + "mph": "mile per hour", + "mpg": "mile per gallon", + } + +# ACRESCENTADO símbolos monetários para a regra currency. +# O inglês não tem R$; acrescentado por ser o caso principal no Brasil. +- CurrencySymbols: { + "R$": "real", + "$": "dólar", "US$": "dólar", "¢": "centavo", "€": "euro", "£": "libra", + "₡": "colón", "₤": "lira", "₨": "rupia", "₩": "won", "₪": "shekel", + "₱": "peso", "₹": "rupia", "₺": "lira", "₿": "bitcoin", + } + +- PluralForms: { + "lúmen": "lúmens", + "kelvin": "kelvins", + "grau Celsius": "graus Celsius", + # O plural genérico acrescenta "s" no FIM da string, o que produziria + # "yoctossegundo de arcos". O "s" tem de ir no substantivo, não na frase. + # (a forma sem prefixo já está mais abaixo, junto de "minuto de arco".) + "centissegundo de arco": "centissegundos de arco", + "milissegundo de arco": "milissegundos de arco", + "microssegundo de arco": "microssegundos de arco", + "nanossegundo de arco": "nanossegundos de arco", + "picossegundo de arco": "picossegundos de arco", + "femtossegundo de arco": "femtossegundos de arco", + "zeptossegundo de arco": "zeptossegundos de arco", + "yoctossegundo de arco": "yoctossegundos de arco", + "rontossegundo de arco": "rontossegundos de arco", + "quectossegundo de arco": "quectossegundos de arco", + "grau Fahrenheit": "graus Fahrenheit", + "hertz": "hertz", + "siemens": "siemens", + "lux": "lux", + "mol": "mols", + # a regra genérica acrescenta "es" a palavras terminadas em l/r, o que + # gera "decibeles"/"molares". Em português o plural de -el é -éis. + "decibel": "decibéis", + "bel": "béis", + + # Unidades invariáveis no plural continuam invariáveis quando prefixadas. + # A regra genérica não consulta o nome composto e produziria + # "milissiemenses", "decahertzes", "attoluxes". + "quettassiemens": "quettassiemens", "ronnassiemens": "ronnassiemens", "yottassiemens": "yottassiemens", "zettassiemens": "zettassiemens", + "exassiemens": "exassiemens", "petassiemens": "petassiemens", "terassiemens": "terassiemens", "gigassiemens": "gigassiemens", + "megassiemens": "megassiemens", "quilossiemens": "quilossiemens", "hectossiemens": "hectossiemens", "decassiemens": "decassiemens", + "decissiemens": "decissiemens", "centissiemens": "centissiemens", "milissiemens": "milissiemens", "microssiemens": "microssiemens", + "nanossiemens": "nanossiemens", "picossiemens": "picossiemens", "femtossiemens": "femtossiemens", "attossiemens": "attossiemens", + "zeptossiemens": "zeptossiemens", "yoctossiemens": "yoctossiemens", "rontossiemens": "rontossiemens", "quectossiemens": "quectossiemens", + "quettahertz": "quettahertz", "ronnahertz": "ronnahertz", "yottahertz": "yottahertz", "zettahertz": "zettahertz", + "exahertz": "exahertz", "petahertz": "petahertz", "terahertz": "terahertz", "gigahertz": "gigahertz", + "megahertz": "megahertz", "quilohertz": "quilohertz", "hectohertz": "hectohertz", "decahertz": "decahertz", + "decihertz": "decihertz", "centihertz": "centihertz", "milihertz": "milihertz", "microhertz": "microhertz", + "nanohertz": "nanohertz", "picohertz": "picohertz", "femtohertz": "femtohertz", "attohertz": "attohertz", + "zeptohertz": "zeptohertz", "yoctohertz": "yoctohertz", "rontohertz": "rontohertz", "quectohertz": "quectohertz", + "quettalux": "quettalux", "ronnalux": "ronnalux", "yottalux": "yottalux", "zettalux": "zettalux", + "exalux": "exalux", "petalux": "petalux", "teralux": "teralux", "gigalux": "gigalux", + "megalux": "megalux", "quilolux": "quilolux", "hectolux": "hectolux", "decalux": "decalux", + "decilux": "decilux", "centilux": "centilux", "mililux": "mililux", "microlux": "microlux", + "nanolux": "nanolux", "picolux": "picolux", "femtolux": "femtolux", "attolux": "attolux", + "zeptolux": "zeptolux", "yoctolux": "yoctolux", "rontolux": "rontolux", "quectolux": "quectolux", + + "mho": "mhos", + "real": "reais", "dólar": "dólares", "colón": "colones", + "pascal": "pascals", + "weber": "webers", + "becquerel": "becquerels", + "katal": "katals", + "rotação por minuto": "rotações por minuto", + "unidade de massa atômica": "unidades de massa atômica", + "unidade astronômica": "unidades astronômicas", + "ano-luz": "anos-luz", + "minuto de arco": "minutos de arco", + "segundo de arco": "segundos de arco", + } + +# ---------------- 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). +# +# NOTA DE TRADUÇÃO -> regra do português para denominador de fração: +# até 1/10 usa-se o ordinal ("um terço", "um décimo"); +# de 1/11 em diante usa-se "avos" ("um onze avos", "três vinte avos"). +# Isso difere do inglês e do espanhol, e está refletido nas listas plurais. + +# All definitions start 0, 10, 100, etc. + +# The definitions for the "ones" should extend until a regular pattern begins +# The minimum length is 10. + +- NumbersOnes: [ + "zero", "um", "dois", "três", "quatro", "cinco", "seis", "sete", "oito", "nove", + "dez", "onze", "doze", "treze", "catorze", "quinze", "dezesseis", + "dezessete", "dezoito", "dezenove" + ] + +- NumbersOrdinalOnes: [ + "zero", "primeiro", "segundo", "terceiro", "quarto", "quinto", "sexto", "sétimo", "oitavo", "nono", + "décimo", "décimo primeiro", "décimo segundo", "décimo terceiro", "décimo quarto", "décimo quinto", "décimo sexto", + "décimo sétimo", "décimo oitavo", "décimo nono" + ] + +- NumbersOrdinalPluralOnes: [ + "zeros", "primeiros", "meios", "terços", "quartos", "quintos", "sextos", "sétimos", "oitavos", "nonos", + "décimos", "onze avos", "doze avos", "treze avos", "catorze avos", "quinze avos", "dezesseis avos", + "dezessete avos", "dezoito avos", "dezenove avos" + ] + + # stop when regularity begins +# A lista parava em "meio": de 1/3 em diante o motor caía nos ordinais +# comuns e falava "1 terceiro". Até 1/10 a forma é a do denominador de +# fração ("um terço", "um décimo"); de 1/11 em diante entra "avos", que já +# está nas listas plurais acima. Ver ACHADOS 9. +- NumbersOrdinalFractionalOnes: [ + "zero", "primeiro", "meio", "terço", "quarto", "quinto", "sexto", "sétimo", "oitavo", "nono", + "décimo" + ] + + # stop when regularity begins +- NumbersOrdinalFractionalPluralOnes: [ + "zeros", "primeiros", "meios", "terços", "quartos", "quintos", "sextos", "sétimos", "oitavos", "nonos", + "décimos" + ] + + + # What to use for multiples of 10 +- NumbersTens: [ + "", "dez", "vinte", "trinta", "quarenta", "cinquenta", "sessenta", "setenta", "oitenta", "noventa" + ] + +- NumbersOrdinalTens: [ + "", "décimo", "vigésimo", "trigésimo", "quadragésimo", "quinquagésimo", "sexagésimo", "septuagésimo", "octogésimo", "nonagésimo" + ] + +- NumbersOrdinalPluralTens: [ + "", "décimos", "vinte avos", "trinta avos", "quarenta avos", "cinquenta avos", "sessenta avos", "setenta avos", "oitenta avos", "noventa avos" + ] + + +- NumbersHundreds: [ + "", "cem", "duzentos", "trezentos", "quatrocentos", "quinhentos", + "seiscentos", "setecentos", "oitocentos", "novecentos" + ] + +- NumbersOrdinalHundreds: [ + "", "centésimo", "ducentésimo", "tricentésimo", "quadringentésimo", "quingentésimo", + "sexcentésimo", "septingentésimo", "octingentésimo", "noningentésimo" + ] + +- NumbersOrdinalPluralHundreds: [ + "", "centésimos", "ducentésimos", "tricentésimos", "quadringentésimos", "quingentésimos", + "sexcentésimos", "septingentésimos", "octingentésimos", "noningentésimos" + ] + + + # At this point, hopefully the language is regular. If not, code needs to be written +- NumbersLarge: [ + "", "mil", "milhão", "bilhão", "trilhão", "quatrilhão", + "quintilhão", "sextilhão", "septilhão", "octilhão", "nonilhão" + ] + +- NumbersOrdinalLarge: [ + "", "milésimo", "milionésimo", "bilionésimo", "trilionésimo", "quatrilionésimo", + "quintilionésimo", "sextilionésimo", "septilionésimo", "octilionésimo", "nonilionésimo" + ] + +- NumbersOrdinalPluralLarge: [ + "", "milésimos", "milionésimos", "bilionésimos", "trilionésimos", "quatrilionésimos", + "quintilionésimos", "sextilionésimos", "septilionésimos", "octilionésimos", "nonilionésimos" + ] diff --git a/Rules/Languages/pt/navigate.yaml b/Rules/Languages/pt/navigate.yaml new file mode 100644 index 000000000..bc7c5fe26 --- /dev/null +++ b/Rules/Languages/pt/navigate.yaml @@ -0,0 +1,1773 @@ +--- +# 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 +# 2. Say info about moving into/out of 2D structures +# 3. Set some variables and possibly recurse. +# If recursing, "MatchCounter" should be incremented. +# 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 + + +# Rules for speaking what happens when moving into or out of a notation +- name: into-or-out-of + tag: mfrac + match: "$Move2D != ''" + replace: + - x: "$Move2D" + - test: + if: "count($Child2D/preceding-sibling::*)=0" + then: [T: "numerador"] + else: [T: "denominador"] + - pause: "medium" + +- name: into-or-out-of + tag: msqrt + match: "$Move2D != ''" + replace: + - x: "$Move2D" + - T: "raiz quadrada" + - pause: "medium" + +- name: into-or-out-of + tag: mroot + match: "$Move2D != ''" + replace: + - x: "$Move2D" + - test: + if: "count($Child2D/preceding-sibling::*)=0" + then: [T: "radicando"] + else: [T: "índice da raiz"] + - pause: "medium" + +- name: into-or-out-of + tag: msub + match: "$Move2D != ''" + replace: + - x: "$Move2D" + - test: + if: "count($Child2D/preceding-sibling::*)=0" + then: [T: "base"] + else: [T: "subscrito"] + - pause: "medium" + +- name: into-or-out-of + tag: msup + match: "$Move2D != ''" + replace: + - x: "$Move2D" + - test: + if: "count($Child2D/preceding-sibling::*)=0" + then: [T: "base"] + else: [T: "sobrescrito"] # FIX: it would be better to use the word used when reading (power, exponent, ...) + - pause: "medium" + +- name: into-or-out-of + tag: msubsup + match: "$Move2D != ''" + replace: + - x: "$Move2D" + - test: + - if: "count($Child2D/preceding-sibling::*)=0" + then: [T: "base"] + - else_if: "count($Child2D/preceding-sibling::*)=1" + then: [T: "subscrito"] + else: [T: "sobrescrito"] # FIX: it would be better to use the word used when reading (power, exponent, ...) + - pause: "medium" + +- name: into-or-out-of + tag: munder + match: "$Move2D != ''" + replace: + - x: "$Move2D" + - test: + if: "count($Child2D/preceding-sibling::*)=0" + then: [T: "base"] + else: [T: "limite inferior"] + - pause: "medium" + +- name: into-or-out-of + tag: mover + match: "$Move2D != ''" + replace: + - x: "$Move2D" + - test: + if: "count($Child2D/preceding-sibling::*)=0" + then: [T: "base"] + else: [T: "limite superior"] + - pause: "medium" + +- name: into-or-out-of + tag: munderover + match: "$Move2D != ''" + replace: + - x: "$Move2D" + - test: + - if: "count($Child2D/preceding-sibling::*)=0" + then: [T: "base"] + - else_if: "count($Child2D/preceding-sibling::*)=1" + then: [T: "limite inferior"] + else: [T: "limite superior"] + - pause: "medium" + +- name: into-or-out-of + tag: mmultiscripts + match: "$Move2D != ''" + replace: + - test: + if: "name($Child2D)!='none'" + then: + - with: + variables: + - NumPrecedingSiblings: "count($Child2D/preceding-sibling::*)" + replace: + - x: "$Move2D" + - test: + - if: "$NumPrecedingSiblings=0" + then: [T: "base"] + - 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: "subscrito"] + else: [T: "sobrescrito"] + else: + - test: + if: "$NumPrecedingSiblings mod 2 = 0" + then: [T: "sobrescrito"] + else: [T: "subscrito"] + - pause: "medium" + +- name: into-or-out-of + tag: mtd + match: "$Move2D = 'em'" + replace: + - x: "$Move2D" + - T: "coluna" + - x: "count($Child2D/preceding-sibling::*)+1" + - pause: "medium" + +- name: into-or-out-of + tag: [mtr, mlabeledtr] + match: "$Move2D = 'em'" + replace: + - x: "$Move2D" + - x: "count($Child2D/preceding-sibling::*)+1" + - 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: "desfazer afastamento"] + - else_if: "$PreviousNavCommand = 'ZoomOut'" + then: [T: "desfazer aproximação"] + - else_if: "$PreviousNavCommand = 'ZoomInAll'" + then: [T: "desfazer afastamento máximo"] + - else_if: "$PreviousNavCommand = 'ZoomOutAll'" + then: [T: "desfazer aproximação máxima"] + - else_if: "$PreviousNavCommand = 'MovePrevious' or $PreviousNavCommand = 'MovePreviousZoom'" + then: [T: "desfazer movimento para a esquerda"] + - else_if: "$PreviousNavCommand = 'MoveNext' or $PreviousNavCommand = 'MoveNextZoom'" + then: [T: "desfazer movimento para a direita"] + - else_if: "$PreviousNavCommand = 'None'" + then: [T: "nenhum comando anterior"] + - pause: "long" + - 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: + - test: + if: "$MatchCounter = 0 and $NavVerbosity != 'Terse'" + then: [T: "aproximado ao máximo", pause: "long"] + - test: + if: "$ReadZoomLevel!=-1" + then: + - set_variables: [ReadZoomLevel: "0"] + - set_variables: [NavNode: "@id"] + +# special case of zooming into a matrix or determinant -- move to the first element +- name: zoom-in-matrix + + tag: mrow + match: + - "$NavCommand = 'ZoomIn' and count(*)=3 and " + - "*[2][self::m:mtable and (IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|'))]" + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: [T: "aproximado", pause: "long"] + - set_variables: [NavNode: "*[2]/*[1]/@id"] + +# special case of zooming into a table -- move to the first element +- name: zoom-in-table + tag: mtable + match: "$NavCommand = 'ZoomIn'" + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: [T: "aproximado", pause: "long"] + - set_variables: [NavNode: "*[1]/@id"] + +- name: zoom-in-mrow-in-math + # Moving to first or last is meaningless the 'math' has only an 'mrow' inside -- dig inside and do it again + tag: math + match: "count(*)=1 and ($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 + # If there is only one child, this isn't an interesting move -- zoom in again + tag: "*" + match: + - "($NavCommand = 'ZoomIn' or " + - " ($NavCommand = 'MoveNextZoom' or $NavCommand = 'MovePreviousZoom') and $NavMode='Enhanced') and " + - "count(*)=1 and (*[1][self::m:mrow] and not(self::m:msqrt or self::m:menclose))" + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: [T: "aproximado", pause: "long"] + - with: + variables: [MatchCounter: "$MatchCounter + 1"] + replace: + - test: + if: "$NavCommand = 'MovePreviousZoom'" + then: [x: "*[last()]"] + else: [x: "*[1]"] + +- name: zoom-in-enhanced + + tag: "*" + match: "$NavCommand = 'ZoomIn' and $NavMode='Enhanced'" + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: [T: "aproximado", pause: "long"] + - test: + - if: "self::m:mtr or self::m:mlabeledtr" + then: + - with: + variables: [Move2D: "'em'", Child2D: "*[1]/*[1]"] + 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: "'em'", Child2D: "*[1]"] + replace: [x: "."] + - set_variables: [NavNode: "*[1]/*[2]/@id"] # skip parens/brackets + else: + - with: + variables: [Move2D: "'em'", Child2D: "*[1]"] + replace: [x: "."] + - set_variables: [NavNode: "*[1]/@id"] + +- name: zoom-in-2D-not-enhanced + tag: "*" + match: "$NavCommand = 'ZoomIn' and $NavMode!='Enhanced' and IsNode(., '2D')" + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: [T: "aproximado", pause: "long"] + - with: + variables: [Move2D: "'em'", Child2D: "*[1]"] + replace: [x: "."] + - with: + variables: [MatchCounter: "$MatchCounter + 1", NavCommand: "'MoveNextZoom'"] + replace: [x: "*[1]"] + +- name: zoom-in-default + tag: "*" + match: "$NavCommand = 'ZoomIn'" + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: [T: "aproximado", pause: "long"] + - with: + variables: [Move2D: "'em'", Child2D: "*[1]"] + replace: [x: "."] + - test: + if: "$NavMode='Character'" + then: + - with: + variables: [MatchCounter: "$MatchCounter + 1"] + replace: [x: "*[1]"] + else_test: + if: "self::m:mtd" + then: [x: "*[1]"] + else: + - test: + if: "$ReadZoomLevel!=-1" + then: + - set_variables: [ReadZoomLevel: "DistanceFromLeaf(*[1], true, $NavMode!='Character')"] + - set_variables: [NavNode: "*[1]/@id"] + +- name: zoom-in-all-default + tag: "*" + match: "$NavCommand = 'ZoomInAll'" + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: [T: "aproximado ao máximo", pause: "medium"] + - with: + variables: [Move2D: "'em'", Child2D: "*[1]"] + replace: [x: "."] + - with: + variables: [MatchCounter: "$MatchCounter + 1"] + replace: [x: "*[1]"] + +- name: zoom-out + tag: math + match: "$NavCommand = 'ZoomOut' or $NavCommand = 'ZoomOutAll'" + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity != 'Terse'" + then: [T: "afastado ao máximo", pause: "long"] + - set_variables: [NavNode: "*[1]/@id"] # no-op for $NavCommand = 'ZoomOut' + +- name: zoom-out-top + tag: "*" + match: + - "($NavCommand = 'ZoomOut' or $NavCommand = 'ZoomOutAll') and" + - "parent::m:math " + replace: + - x: ".." # let math rule deal with it + +- 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-all-default + tag: "*" + match: "$NavCommand = 'ZoomOutAll'" + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity != 'Terse'" + then: [T: "afastado ao máximo", pause: "medium"] + - with: + variables: [Move2D: "'fora de'", Child2D: "."] + replace: [x: ".."] + - with: + variables: [MatchCounter: "$MatchCounter + 1"] + replace: [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: + # don't bother with MatchCounter since we only get here if > 1 + - with: + variables: [Move2D: "'em'", Child2D: "*[1]"] + replace: [x: "."] + - test: + - if: "count(*)> 1 or IsNode(., 'leaf') or self::m:msqrt or self::m:menclose" + then: [set_variables: [NavNode: "@id"]] + 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: "$ReadZoomLevel >= DistanceFromLeaf(., false, $NavMode!='Character')" + then: + # - with: + # variables: [Move2D: "'em'", Child2D: "following-sibling::*[1]"] + # replace: [x: ".."] + - set_variables: [NavNode: "@id"] + else: + - with: + variables: [Move2D: "'em'", Child2D: "*[1]"] + 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: "'em'", Child2D: "preceding-sibling::*[1]"] + # replace: [x: ".."] + - set_variables: [NavNode: "@id"] + else: + - with: + variables: [Move2D: "'em'", Child2D: "*[last()]"] + replace: [x: "."] + - x: "*[last()]" + +# ********* ZoomOut *************** +- name: zoom-out-default + + + tag: mtd + match: "$Move2D = '' and ($NavCommand = 'ZoomOut')" + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: [T: "afastar", pause: "medium"] + # we need to speak it here + - T: "linha" + # 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"] + +- 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: [MatchCounter: "$MatchCounter + 1"] + replace: + - test: + if: "$MatchCounter = 1 and $NavVerbosity = 'Verbose'" + then: [T: "afastar", pause: "medium"] + - test: + 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: "'fora de'", Child2D: "."] + replace: [x: ".."] + - test: + if: "parent::m:mtd" + then: [x: ".."] + else: + - test: + if: "$ReadZoomLevel!=-1" + then: [set_variables: [ReadZoomLevel: "DistanceFromLeaf(.., true, $NavMode!='Character')"]] + - set_variables: [NavNode: "../@id"] + +# ********* 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: "início da expressão"] + - else_if: "$NavCommand = 'MoveLineStart'" + then: [T: "mover para o início da linha"] + - else_if: "$NavCommand = 'MoveEnd'" + then: [T: "fim da expressão"] + else: [T: "mover para o fim da linha"] # "$NavCommand = 'MoveLineEnd'" + - 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) + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "*[1]/*[1]"] + else: + - with: + variables: [NavCommand: "'MovePreviousZoom'"] + replace: [x: "*[last()]/*[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::m:msub or parent::m:msup or parent::m:msubsup or parent::m:mmultiscripts) )" + replace: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - test: + if: "$NavCommand = 'MoveLineStart'" + then: [T: "mover para o início da linha"] + else: [T: "mover para o fim da linha"] # "$NavCommand = 'MoveLineEnd'" + - pause: "medium" + - test: + if: "self::m: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: "fora da tabela" + - 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: "mover para a esquerda" + - pause: short + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "coluna" + - 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: "não há coluna anterior" + - 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: "mover para a direita" + - pause: short + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "coluna" + - 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: "não há próxima coluna" + - 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: "mover para cima" + - pause: short + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "linha" + - x: "count(../preceding-sibling::*)" + - pause: short + - T: "coluna" + - 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: "não há linha anterior" + - 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: "descer" + - pause: short + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "linha" + - x: "count(../preceding-sibling::*)+2" + - pause: short + - T: "coluna" + - 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: "não há próxima linha" + - set_variables: [SpeakExpression: false()] + +- name: move-cell-up + tag: [mtr, mlabeledtr] + match: "$NavCommand='MoveCellUp'" + replace: + - test: + if: "preceding-sibling::*" + then: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - T: "subir" + - pause: medium + - T: "linha" + - x: "count(preceding-sibling::*)" + - pause: medium + - test: + if: "$NavMode='Character'" + then: + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "preceding-sibling::*[1]"] + else: + - set_variables: [NavNode: "preceding-sibling::*[1]/@id"] + else: + - T: "não há linha anterior" + - set_variables: [SpeakExpression: false()] + +- name: move-cell-down + tag: [mtr, mlabeledtr] + match: "$NavCommand='MoveCellDown'" + replace: + - test: + if: "following-sibling::*" + then: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - T: "descer" + - pause: medium + - T: "linha" + - 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]/@id"] + else: + - T: "não há próxima linha" + - 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: "ler a entrada atual" + - pause: medium + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "linha" + - x: "count($MTD[1]/../preceding-sibling::*)+1" + - T: "coluna" + - x: "count($MTD[1]/preceding-sibling::*)+1" + - pause: short + - set_variables: [NavNode: "$MTD[1]/*[1]/@id"] + else: + - T: "fora da tabela" + - 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: "fora da tabela" + - pause: long + - set_variables: [SpeakExpression: false()] + +# ======== Move/Read/Describe Next rules ================= + +# 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: "'em'", Child2D: "$Following"] + replace: [x: ".."] + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "$Following"] + +# 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: "'em'", Child2D: "$Preceding"] + replace: [x: ".."] + - with: + variables: [NavCommand: "'MovePreviousZoom'"] + replace: [x: "$Preceding"] + + + +# 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: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: + - test: + - if: "$NavCommand = 'MoveNext'" + then: [T: "ir"] + - else_if: "$NavCommand = 'ReadNext'" + then: [T: "ler"] + else: [T: "descrever"] + - T: "direita" + - pause: short + - with: + variables: [MatchCounter: "$MatchCounter + 1"] + replace: + - test: + if: "following-sibling::*[1][.='\u2062' or .='\u2064'] and $NavMode='Enhanced'" # 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 != 'Terse' and $NavCommand = 'MoveNext'" + then: + - T: "não é possível mover para a direita" + - with: + variables: [Move2D: "'fim de'", Child2D: "$EdgeNode/*[last()]"] + 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 != 'Terse'" + then: + - T: "não é possível" + - test: + - if: "$NavCommand = 'MoveNext'" + then: [T: "mover"] + - else_if: "$NavCommand = 'ReadNext'" + then: [T: "ler"] + else: [T: "descrever"] + - T: "direita, fim da expressão" + - 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: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: + - test: + - if: "$NavCommand = 'MoveNext'" + then: [T: "mover"] + - else_if: "$NavCommand = 'ReadNext'" + then: [T: "ler"] + else: [T: "descrever"] + - T: "direita" + - pause: short + - with: + variables: [MatchCounter: "$MatchCounter + 1", NavCommand: "'MoveNext'"] + replace: + - test: + if: "following-sibling::*" + then: + - with: + variables: [Move2D: "'em'", Child2D: "."] + replace: [x: ".."] + else: + - with: + variables: [Move2D: "'fora de'", Child2D: "."] + replace: [x: ".."] + - test: + if: "following-sibling::*" + then: + - x: ".." # move out of parens + else: + - 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: + - test: + # can't get here with MatchCounter=0, so no need to echo command + if: "following-sibling::*" + then: + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "coluna" + - 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')" + replace: + - test: + # can't get here with MatchCounter=0, so no need to echo command + if: "following-sibling::*" + then: + - T: "linha" + - x: "count(preceding-sibling::*)+2" + - T: "coluna 1" + - pause: medium + - test: + if: "$NavMode = 'Character'" + then: + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "following-sibling::*[1]"] + else: + - set_variables: [NavNode: "following-sibling::*[1]/*[1]/*[1]/@id"] + else: [x: ".."] # try again for after + +- 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: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: + - test: + - if: "$NavCommand = 'MoveNext'" + then: [T: mover] + - else_if: "$NavCommand = 'ReadNext'" + then: [T: "ler"] + else: [T: "descrever"] + - T: "direita" + - pause: short + - 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: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: + - test: + - if: "$NavCommand = 'MoveNext'" + then: [T: mover] + - else_if: "$NavCommand = 'ReadNext'" + then: [T: "ler"] + else: [T: "descrever"] + - T: "direita" + - pause: short + - 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: "'fora de'", Child2D: "."] + replace: [x: ".."] + - with: + variables: [MatchCounter: "$MatchCounter + 1"] + replace: [x: "following-sibling::*[1]"] # skip over 'none' + else: + - with: + variables: [Move2D: "'em'", Child2D: "following-sibling::*[1]"] + replace: [x: ".."] + - with: + variables: [MatchCounter: "$MatchCounter + 1", NavCommand: "'MoveNextZoom'"] + replace: [x: "following-sibling::*[1]"] + +- name: move-next-default + tag: "*" + match: "$NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext'" + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: + - test: + - if: "$NavCommand = 'MoveNext'" + then: [T: mover] + - else_if: "$NavCommand = 'ReadNext'" + then: [T: "ler"] + else: [T: "descrever"] + - T: "direita" + - pause: short + - test: + if: "IsNode(.., '2D')" + then: + - with: + variables: [Move2D: "'em'", Child2D: "following-sibling::*[1]"] + replace: [x: ".."] + - set_variables: [NavNode: "following-sibling::*[1]/@id"] + +# ======== Move/Read/Describe Previous rules ================= + +# 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: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: + - test: + - if: "$NavCommand = 'MovePrevious'" + then: [T: "mover"] + - else_if: "$NavCommand = 'ReadPrevious'" + then: [T: "ler"] + else: [T: "descrever"] + - T: "esquerda" + - pause: short + - with: + variables: [MatchCounter: "$MatchCounter + 1"] + replace: + - 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 right +- name: move-previous-no-auto-zoom-at-edge + # at edge of 2D and in a mode where moving right 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 != 'Terse' and $NavCommand = 'MovePrevious'" + then: + - T: "não é possível mover para a esquerda" + - with: + variables: [Move2D: "'fim de'", Child2D: "$EdgeNode/*[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: "início da expressão" + - pause: long + - set_variables: [SpeakExpression: false()] + +- name: move-previous-at-end + + tag: "*" + match: + - "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and" + - "EdgeNode(., 'left', 'math')/@id!=@id" # at edge of math + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity != 'Terse'" + then: + - T: "não é possível mover para a esquerda, início da expressão" + - pause: long + - set_variables: [SpeakExpression: false()] + +- 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: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: + - test: + - if: "$NavCommand = 'MovePrevious'" + then: [T: "mover"] + - else_if: "$NavCommand = 'ReadPrevious'" + then: [T: "ler"] + else: [T: "descrever"] + - T: "esquerda" + - pause: short + - with: + variables: [MatchCounter: "$MatchCounter + 1", NavCommand: "'MovePrevious'"] + replace: + - test: + if: "preceding-sibling::*" + then: + - with: + variables: [Move2D: "'em'", Child2D: "."] + replace: [x: ".."] + else: + - with: + variables: [Move2D: "'fora de'", Child2D: "."] + replace: [x: ".."] + - test: + if: "preceding-sibling::*" + then: + - x: .. # move out of parens + else: + - x: ".." + +- name: move-previous-auto-zoom-parens + # auto-zoom into next child if next child is parenthesized expr + # 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: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: + - test: + - if: "$NavCommand = 'MovePrevious'" + then: [T: mover] + - else_if: "$NavCommand = 'ReadPrevious'" + then: [T: "ler"] + else: [T: "descrever"] + - T: "esquerda" + - pause: short + - test: + if: "not(parent::m:mrow)" + then: + - with: + variables: [Move2D: "'em'", Child2D: "preceding-sibling::*[1]"] + 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')" + replace: + - test: + # can't get here with MatchCounter=0, so no need to echo command + if: "preceding-sibling::*" + then: + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "coluna" + - 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"]] + else: + - x: ".." # try again at the row level + +- name: move-previous-default + tag: [mtr, mlabeledtr] + match: "$Move2D = '' and ($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious')" + replace: + - test: + # can't get here with MatchCounter=0, so no need to echo command + if: "preceding-sibling::*" + then: + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "linha" + - x: "count(preceding-sibling::*)" + - T: "coluna" + - x: "count(*)" + - pause: medium + - test: + if: "$NavMode = 'Character'" + then: + - with: + variables: [NavCommand: "'MovePreviousZoom'"] + replace: [x: "preceding-sibling::*[1]"] + else: + - set_variables: [NavNode: "preceding-sibling::*[1]/*[last()]/*[last()]/@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: + - 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: "'fora de'", Child2D: "."] + replace: [x: ".."] + - with: + variables: [MatchCounter: "$MatchCounter + 1"] + replace: [x: "preceding-sibling::*[1]"] # skip over 'none' + else: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: + - test: + - if: "$NavCommand = 'MovePrevious'" + then: [T: mover] + - else_if: "$NavCommand = 'ReadPrevious'" + then: [T: "ler"] + else: [T: "descrever"] + - T: "esquerda" + - pause: short + - with: + variables: [Move2D: "'em'", Child2D: "preceding-sibling::*[1]"] + replace: [x: ".."] + - with: + variables: [MatchCounter: "$MatchCounter + 1", NavCommand: "'MovePreviousZoom'"] + replace: [x: "preceding-sibling::*[1]"] + +- name: move-previous-default + tag: "*" + match: "$NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious'" + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: + - test: + - if: "$NavCommand = 'MovePrevious'" + then: [T: mover] + - else_if: "$NavCommand = 'ReadPrevious'" + then: [T: "ler"] + else: [T: "descrever"] + - T: "esquerda" + - pause: short + - test: + if: "IsNode(.., '2D')" + then: + - with: + variables: [Move2D: "'em'", Child2D: "preceding-sibling::*[1]"] + 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: "caractere" + - set_variables: [NavMode: "'Character'", ReadZoomLevel: "1"] + - else_if: "$NavMode = 'Character'" + then: + - T: "simples" + - set_variables: [NavMode: "'Simple'", ReadZoomLevel: "1"] + - else: + - T: "avançado" + - set_variables: [NavMode: "'Enhanced'", ReadZoomLevel: "-1"] + - T: "modo" + - 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: "simples" + - set_variables: [NavMode: "'Simple'", ReadZoomLevel: "1"] + - else_if: "$NavMode = 'Character'" + then: + - T: "avançado" + - set_variables: [NavMode: "'Enhanced'", ReadZoomLevel: "-1"] + - else: + - T: "caractere" + - set_variables: [NavMode: "'Character'", ReadZoomLevel: "1"] + - T: "modo" + - 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: "falar a expressão após mover" + - set_variables: [Overview: "'false'"] + else: + - T: "resumo da expressão após mover" + - set_variables: [Overview: "'true'"] + - pause: long + +- name: current + tag: "*" + match: "$NavCommand = 'ReadCurrent' or $NavCommand = 'DescribeCurrent'" + replace: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - test: + - if: "$NavCommand = 'ReadCurrent'" + then: [T: "ler"] + else: [T: "descrever"] + - T: "atual" + - set_variables: [NavNode: "@id"] + - pause: long + +# 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: "ler"] + - else_if: "starts-with($NavCommand, 'Describe')" + then: [T: "descrever"] + - else_if: "starts-with($NavCommand, 'MoveTo')" + then: [T: "mover para"] + else: [T: "definir"] + - T: "marcador de posição" + - x: "$PlaceMarkerIndex" + - pause: long + - set_variables: [NavNode: "$PlaceMarker"] + +- name: set-placemarker + tag: "*" + match: "starts-with($NavCommand, 'SetPlacemarker')" + replace: + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "inserir marcador de posição" + - 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: "não há mais nada dentro" + - pause: long + - set_variables: [SpeakExpression: false()] + else: + - T: "interior" + - pause: medium + - set_variables: [NavNode: "@id"] + +- name: where-am-i-middle + tag: "*" + match: "$NavCommand = 'WhereAmI' or $NavCommand = 'WhereAmIAll'" + replace: + - T: "interior" + - 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/pt/overview.yaml b/Rules/Languages/pt/overview.yaml new file mode 100644 index 000000000..f28a44564 --- /dev/null +++ b/Rules/Languages/pt/overview.yaml @@ -0,0 +1,119 @@ +--- +# 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: [fraction, mfrac] + match: "." + replace: + - test: + if: "IsNode(*[1], 'simple') and IsNode(*[2], 'simple')" + then: + - x: "*[1]" + - T: "sobre" + - x: "*[2]" + else: + - T: "fração" + +- name: overview-default + tag: [msqrt, "square-root"] # CORREÇÃO: idem + match: "." + replace: + - T: "raiz quadrada" + - test: + if: "IsNode(*[1], 'simple')" + then: + - test: + if: "$Verbosity!='Terse'" + then: [T: de] + - x: "*[1]" + +- name: overview-default + tag: [mroot, root] # CORREÇÃO: idem + match: "." + replace: + - test: + if: "*[2][self::m:mn]" + then_test: + - if: "*[2][.='2']" + then: [T: raiz quadrada] + - else_if: "*[2][.='3']" + then: [T: raiz cúbica] + - else_if: "*[2][not(contains(., '.'))]" + then: [T: raiz, T: de índice, x: "*[2]"] + else: + # índice simbólico (ex.: raiz de índice n) — em português a ordem natural + # é "raiz de índice n", diferente do inglês "n-th root". + - T: raiz + - T: de índice + - x: "*[2]" + - test: + if: "IsNode(*[1], 'simple')" + then: + - test: + if: "$Verbosity!='Terse'" + then: [T: de] + - x: "*[1]" + +- name: matrix-override + tag: mrow + match: + - "*[2][self::m:mtable] and" + - "(IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|'))" + replace: + # CORREÇÃO: a ordem herdada do inglês ("the 2 by 3 matrix") produzia + # "a 2 por 3 matriz". Em português o substantivo vem antes da dimensão. + - test: + if: "*[1][.='|']" # just need to check the first bracket since we know it must be (, [, or | + then: [T: determinante] + else: [T: matriz] + - x: count(*[2]/*) + - T: por + - x: count(*[2]/*[self::m:mtr][1]/*) + +- name: overview-default + tag: mtable + match: "." + replace: + # CORREÇÃO: mesma inversão de ordem do bloco acima. + - T: "tabela" + - x: count(*[2]/*) + - T: por + - x: count(*[2]/*[self::m:mtr][1]/*) + +- 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: "etc." + +- include: "SimpleSpeak_Rules.yaml" diff --git a/Rules/Languages/pt/unicode-full.yaml b/Rules/Languages/pt/unicode-full.yaml new file mode 100644 index 000000000..21a1e0b71 --- /dev/null +++ b/Rules/Languages/pt/unicode-full.yaml @@ -0,0 +1,3431 @@ + + - "˘": [T: "breve"] # 0x2d8 + - "˚": [T: "anel acima"] # 0x2da + - "˛": [T: "ogonek"] # 0x2db + - "˜": [T: "til pequeno"] # 0x2dc + - "˝": [T: "acento agudo duplo"] # 0x2dd + - "↔": [T: "seta para a esquerda e para a direita"] # 0x2194 + - "↕": [T: "seta para cima e para baixo"] # 0x2195 + - "↖": [T: "seta para noroeste"] # 0x2196 + - "↙": [T: "seta para sudoeste"] # 0x2199 + - "↚": [T: "seta para a esquerda com traço"] # 0x219a + - "↛": [T: "seta para a direita com traço"] # 0x219b + - "↜": [T: "seta ondulada para a esquerda"] # 0x219c + - "↝": [T: "seta ondulada para a direita"] # 0x219d + - "↞": [T: "seta de duas pontas para a esquerda"] # 0x219e + - "↟": [T: "seta de duas pontas para cima"] # 0x219f + - "↠": [T: "seta de duas pontas para a direita"] # 0x21a0 + - "↡": [T: "seta de duas pontas para baixo"] # 0x21a1 + - "↢": [T: "seta para a esquerda com cauda"] # 0x21a2 + - "↣": [T: "seta para a direita com cauda"] # 0x21a3 + - "↤": [T: "seta para a esquerda a partir de barra"] # 0x21a4 + - "↥": [T: "seta para cima a partir de barra"] # 0x21a5 + - "↦": [T: "seta para a direita a partir de barra"] # 0x21a6 + - "↧": [T: "seta para baixo a partir de barra"] # 0x21a7 + - "↨": [T: "seta para cima e para baixo com base"] # 0x21a8 + - "↩": [T: "seta para a esquerda com gancho"] # 0x21a9 + - "↪": [T: "seta para a direita com gancho"] # 0x21aa + - "↫": [T: "seta para a esquerda com laço"] # 0x21ab + - "↬": [T: "seta para a direita com laço"] # 0x21ac + - "↭": [T: "seta ondulada para a esquerda e para a direita"] # 0x21ad + - "↮": [T: "seta para a esquerda e para a direita com traço"] # 0x21ae + - "↯": [T: "seta em zigue-zague para baixo"] # 0x21af + - "↰": [T: "seta para cima com ponta para a esquerda"] # 0x21b0 + - "↱": [T: "seta para cima com ponta para a direita"] # 0x21b1 + - "↲": [T: "seta para baixo com ponta para a esquerda"] # 0x21b2 + - "↳": [T: "seta para baixo com ponta para a direita"] # 0x21b3 + - "↴": [T: "seta para a direita com canto para baixo"] # 0x21b4 + - "↵": [T: "seta para baixo com canto para a esquerda"] # 0x21b5 + - "↶": [T: "seta semicircular superior no sentido anti-horário"] # 0x21b6 + - "↷": [T: "seta semicircular superior no sentido horário"] # 0x21b7 + - "↸": [T: "seta para noroeste até barra longa"] # 0x21b8 + - "↹": [T: "seta para a esquerda até barra sobre seta para a direita até barra"] # 0x21b9 + - "↺": [T: "seta circular aberta no sentido anti-horário"] # 0x21ba + - "↻": [T: "seta circular aberta no sentido horário"] # 0x21bb + - "↼": [T: "arpão para a esquerda com farpa para cima"] # 0x21bc + - "↽": [T: "arpão para a esquerda com farpa para baixo"] # 0x21bd + - "↾": [T: "arpão para cima com farpa para a direita"] # 0x21be + - "↿": [T: "arpão para cima com farpa para a esquerda"] # 0x21bf + - "⇀": [T: "arpão para a direita com farpa para cima"] # 0x21c0 + - "⇁": [T: "arpão para a direita com farpa para baixo"] # 0x21c1 + - "⇂": [T: "arpão para baixo com farpa para a direita"] # 0x21c2 + - "⇃": [T: "arpão para baixo com farpa para a esquerda"] # 0x21c3 + - "⇄": [T: "seta para a direita sobre seta para a esquerda"] # 0x21c4 + - "⇅": [T: "seta para cima à esquerda de seta para baixo"] # 0x21c5 + - "⇆": [T: "seta para a esquerda sobre seta para a direita"] # 0x21c6 + - "⇇": [T: "duas setas para a esquerda"] # 0x21c7 + - "⇈": [T: "duas setas para cima"] # 0x21c8 + - "⇉": [T: "duas setas para a direita"] # 0x21c9 + - "⇊": [T: "duas setas para baixo"] # 0x21ca + - "⇋": [T: "arpão para a esquerda sobre arpão para a direita"] # 0x21cb + - "⇌": [T: "arpão para a direita sobre arpão para a esquerda"] # 0x21cc + - "⇍": [T: "seta dupla para a esquerda com traço"] # 0x21cd + - "⇎": [T: "seta dupla para a esquerda e para a direita com traço"] # 0x21ce + - "⇏": [T: "seta dupla para a direita com traço"] # 0x21cf + - "⇐": [T: "seta dupla para a esquerda"] # 0x21d0 + - "⇑": [T: "seta dupla para cima"] # 0x21d1 + - "⇓": [T: "seta dupla para baixo"] # 0x21d3 + - "⇔": [T: "se e somente se"] # 0x21d4 + - "⇕": [T: "seta dupla para cima e para baixo"] # 0x21d5 + - "⇖": [T: "seta dupla para noroeste"] # 0x21d6 + - "⇗": [T: "seta dupla para nordeste"] # 0x21d7 + - "⇘": [T: "seta dupla para sudeste"] # 0x21d8 + - "⇙": [T: "seta dupla para sudoeste"] # 0x21d9 + - "⇚": [T: "seta tripla para a esquerda"] # 0x21da + - "⇛": [T: "seta tripla para a direita"] # 0x21db + - "⇜": [T: "seta serpenteante para a esquerda"] # 0x21dc + - "⇝": [T: "seta serpenteante para a direita"] # 0x21dd + - "⇞": [T: "seta para cima com traço duplo"] # 0x21de + - "⇟": [T: "seta para baixo com traço duplo"] # 0x21df + - "⇠": [T: "seta tracejada para a esquerda"] # 0x21e0 + - "⇡": [T: "seta tracejada para cima"] # 0x21e1 + - "⇢": [T: "seta tracejada para a direita"] # 0x21e2 + - "⇣": [T: "seta tracejada para baixo"] # 0x21e3 + - "⇤": [T: "seta para a esquerda até barra"] # 0x21e4 + - "⇥": [T: "seta para a direita até barra"] # 0x21e5 + - "⇦": [T: "seta vazada para a esquerda"] # 0x21e6 + - "⇧": [T: "seta vazada para cima"] # 0x21e7 + - "⇨": [T: "seta vazada para a direita"] # 0x21e8 + - "⇩": [T: "seta vazada para baixo"] # 0x21e9 + - "⇪": [T: "seta vazada para cima a partir de barra"] # 0x21ea + - "⇫": [T: "seta vazada para cima sobre pedestal"] # 0x21eb + - "⇬": [T: "seta vazada para cima sobre pedestal com barra horizontal"] # 0x21ec + - "⇭": [T: "seta vazada para cima sobre pedestal com barra vertical"] # 0x21ed + - "⇮": [T: "seta vazada dupla para cima"] # 0x21ee + - "⇯": [T: "seta vazada dupla para cima sobre pedestal"] # 0x21ef + - "⇰": [T: "seta vazada para a direita a partir de parede"] # 0x21f0 + - "⇱": [T: "seta para noroeste até o canto"] # 0x21f1 + - "⇲": [T: "seta para sudeste até o canto"] # 0x21f2 + - "⇳": [T: "seta vazada para cima e para baixo"] # 0x21f3 + - "⇴": [T: "seta para a direita com círculo pequeno"] # 0x21f4 + - "⇵": [T: "seta para baixo à esquerda de seta para cima"] # 0x21f5 + - "⇶": [T: "três setas para a direita"] # 0x21f6 + - "⇷": [T: "seta para a esquerda com traço vertical"] # 0x21f7 + - "⇸": [T: "seta para a direita com traço vertical"] # 0x21f8 + - "⇹": [T: "seta para a esquerda e para a direita com traço vertical"] # 0x21f9 + - "⇺": [T: "seta para a esquerda com traço vertical duplo"] # 0x21fa + - "⇻": [T: "seta para a direita com traço vertical duplo"] # 0x21fb + - "⇼": [T: "seta para a esquerda e para a direita com traço vertical duplo"] # 0x21fc + - "⇽": [T: "seta de ponta aberta para a esquerda"] # 0x21fd + - "⇾": [T: "seta de ponta aberta para a direita"] # 0x21fe + - "⇿": [T: "seta vazada para a esquerda e para a direita"] # 0x21ff + - "∀": [T: "para todo"] # 0x2200 + - "∁": # 0x2201 + - test: + if: "$Verbosity!='Terse'" + then: [OT: "o"] + - T: "complemento de" + - "∃": [T: "existe"] # 0x2203 + - "∄": [T: "não existe"] # 0x2204 + - "∅": [T: "conjunto vazio"] # 0x2205 + - "∇": [T: "nabla"] # 0x2207 + - "∋": [T: "contém o elemento"] # 0x220b + - "∌": [T: "não contém o elemento"] # 0x220c + - "∍": [T: "contém o elemento"] # 0x220d + - "∎": [T: "fim da demonstração"] # 0x220e + - "∏": [T: "produtório"] # 0x220f + - "∑": [T: "somatório"] # 0x2211 + - "−": [T: "menos"] # 0x2212 + - "∓": [T: "menos ou mais"] # 0x2213 + - "∔": [T: "mais com ponto"] # 0x2214 + - "∕": [T: "dividido por"] # 0x2215 + - "∖": [T: "diferença de conjuntos"] # 0x2216 + - "∗": [T: "vezes"] # 0x2217 + - "∙": # 0x2219 + - test: + if: "@data-chem-formula-op" + then: [T: "ponto"] + else: [T: "vezes"] + - "∛": # 0x221b + - test: + if: "$Verbosity!='Terse'" + then: [OT: "a"] + - T: "raiz cúbica de" + - "∜": # 0x221c + - test: + if: "$Verbosity!='Terse'" + then: [OT: "a"] + - T: "raiz quarta de" + - "∞": [T: "infinito"] # 0x221e + - "∟": [T: "ângulo reto"] # 0x221f + - "∠": [T: "ângulo"] # 0x2220 + - "∡": [T: "ângulo medido"] # 0x2221 + - "∢": [T: "ângulo esférico"] # 0x2222 + - "∧": [T: "e"] # 0x2227 + - "∨": [T: "ou"] # 0x2228 + - "∩": [T: "interseção"] # 0x2229 + - "∪": [T: "união"] # 0x222a + - "∫": [T: "integral"] # 0x222b + - "∬": [T: "integral dupla"] # 0x222c + - "∭": [T: "integral tripla"] # 0x222d + - "∮": [T: "integral de contorno"] # 0x222e + - "∯": [T: "integral de superfície"] # 0x222f + - "∰": [T: "integral de volume"] # 0x2230 + - "∱": [T: "integral no sentido horário"] # 0x2231 + - "∲": [T: "integral de contorno no sentido horário"] # 0x2232 + - "∳": [T: "integral de contorno no sentido anti-horário"] # 0x2233 + - "∴": [T: "portanto"] # 0x2234 + - "∵": [T: "porque"] # 0x2235 + - "∸": [T: "menos com ponto"] # 0x2238 + - "∹": [T: "tem excesso em relação a"] # 0x2239 + - "∺": # 0x223a + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "geometricamente proporcional a" + - "∻": # 0x223b + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "homotético a" + - "≀": [T: "produto coroa"] # 0x2240 + - "≁": [T: "não é semelhante a"] # 0x2241 + - "≂": [T: "menos til"] # 0x2242 + - "≃": # 0x2243 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "assintoticamente igual a" + - "≄": # 0x2244 + # audit-ignore: em português a negação já é frase completa; o "is" do inglês não tem par que valha suprimir no Terse + - T: "não é assintoticamente igual a" + - "≅": # 0x2245 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "congruente a" + - "≆": # 0x2246 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "aproximadamente mas não realmente igual a" + - "≇": # 0x2247 + + - T: "não é congruente a" + - "≈": # 0x2248 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "aproximadamente igual a" + - "≉": # 0x2249 + # audit-ignore: em português a negação já é frase completa; o "is" do inglês não tem par que valha suprimir no Terse + - T: "não é aproximadamente igual a" + - "≊": # 0x224a + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "aproximadamente igual ou igual a" + - "≋": [T: "til triplo"] # 0x224b + - "≌": [T: "são todos iguais a"] # 0x224c + - "≍": # 0x224d + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "equivalente a" + - "≎": # 0x224e + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "geometricamente equivalente a" + - "≏": # 0x224f + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "a diferença entre" + - "≐": [T: "aproxima-se do limite"] # 0x2250 + - "≑": # 0x2251 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "geometricamente igual a" + - "≒": # 0x2252 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "aproximadamente igual ou a imagem de" + - "≓": # 0x2253 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "a imagem de ou aproximadamente igual a" + - "≔": [T: "dois pontos igual"] # 0x2254 + - "≕": [T: "igual dois pontos"] # 0x2255 + - "≖": [T: "igual com anel"] # 0x2256 + - "≗": # 0x2257 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "igual por definição a" + - "≘": [T: "corresponde a"] # 0x2258 + - "≙": [T: "estima"] # 0x2259 + - "≚": # 0x225a + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "equiangular a" # era "igual e inclinado a" (descrição do glifo); en: 'is equiangular to' + - "≛": [T: "igual com estrela"] # 0x225b + - "≜": [T: "igual delta"] # 0x225c + - "≝": [T: "é definido como"] # 0x225d + - "≞": # 0x225e + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "medido por" # era "igual por medida a" (descrição); en: 'is measured by' + - "≟": [T: "tem relação desconhecida com"] # 0x225f + - "≢": # 0x2262 + # audit-ignore: em português a negação já é frase completa; o "is" do inglês não tem par que valha suprimir no Terse + - T: "não é idêntico a" + - "≣": # 0x2263 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "estritamente equivalente a" + - "≨": # 0x2268 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "menor que mas não igual a" + - "≩": # 0x2269 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "maior que mas não igual a" + - "≬": # 0x226c + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "entre" + - "≭": # 0x226d + # audit-ignore: em português a negação já é frase completa; o "is" do inglês não tem par que valha suprimir no Terse + - T: "não é equivalente a" + - "≮": # 0x226e + # audit-ignore: em português a negação já é frase completa; o "é" do + # inglês (is not less than) não tem par que valha suprimir no Terse + - T: "não é menor que" + - "≯": # 0x226f + # audit-ignore: em português a negação já é frase completa; o "is" do inglês não tem par que valha suprimir no Terse + - T: "não é maior que" + - "≰": # 0x2270 + # audit-ignore: em português a negação já é frase completa; o "is" do inglês não tem par que valha suprimir no Terse + - T: "não é menor nem igual a" + - "≱": # 0x2271 + # audit-ignore: em português a negação já é frase completa; o "is" do inglês não tem par que valha suprimir no Terse + - T: "não é maior nem igual a" + - "≲": # 0x2272 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "menor ou equivalente a" + - "≳": # 0x2273 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "maior ou equivalente a" + - "≴": # 0x2274 + # audit-ignore: em português a negação já é frase completa; o "is" do inglês não tem par que valha suprimir no Terse + - T: "não é menor nem equivalente a" + - "≵": # 0x2275 + # audit-ignore: em português a negação já é frase completa; o "is" do inglês não tem par que valha suprimir no Terse + - T: "não é maior nem equivalente a" + - "≶": # 0x2276 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "menor ou maior que" + - "≷": # 0x2277 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "maior ou menor que" + - "≸": # 0x2278 + # audit-ignore: em português a negação já é frase completa; o "is" do inglês não tem par que valha suprimir no Terse + - T: "nem menor nem maior que" + - "≹": # 0x2279 + # audit-ignore: em português a negação já é frase completa; o "is" do inglês não tem par que valha suprimir no Terse + - T: "nem maior nem menor que" + - "≺": [T: "precede"] # 0x227a + - "≻": [T: "sucede"] # 0x227b + - "≼": [T: "precede ou é igual a"] # 0x227c + - "≽": [T: "sucede ou é igual a"] # 0x227d + - "≾": [T: "precede ou é equivalente a"] # 0x227e + - "≿": [T: "sucede ou é equivalente a"] # 0x227f + - "⊀": [T: "não precede"] # 0x2280 + - "⊁": [T: "não sucede"] # 0x2281 + - "⊈": # 0x2288 + # audit-ignore: negação já é frase completa; com o ramo "é" saía + # "é não está contido em nem é igual a". Igual a ≢, ≇ e ∦. + - T: "não está contido em nem é igual a" + - "⊉": # 0x2289 + # audit-ignore: idem ⊈ — saía "é não contém nem é igual a". + - T: "não contém nem é igual a" + - "⊊": [T: "está contido em mas não é igual a"] # 0x228a + - "⊋": [T: "contém mas não é igual a"] # 0x228b + - "⊌": [T: "multiconjunto"] # 0x228c + - "⊍": [T: "multiplicação de multiconjunto"] # 0x228d + - "⊎": [T: "união de multiconjunto"] # 0x228e + - "⊏": [T: "imagem quadrada de"] # 0x228f + - "⊐": [T: "original quadrado de"] # 0x2290 + - "⊑": [T: "imagem quadrada de ou igual a"] # 0x2291 + - "⊒": [T: "original quadrado de ou igual a"] # 0x2292 + - "⊓": [T: "interseção quadrada"] # 0x2293 + - "⊔": [T: "união quadrada"] # 0x2294 + - "⊕": [T: "mais circulado"] # 0x2295 + - "⊖": [T: "menos circulado"] # 0x2296 + - "⊗": [T: "vezes circulado"] # 0x2297 + - "⊘": [T: "barra circulada"] # 0x2298 + - "⊙": [T: "ponto circulado"] # 0x2299 + - "⊚": [T: "anel circulado"] # 0x229a + - "⊛": [T: "asterisco circulado"] # 0x229b + - "⊜": [T: "igual circulado"] # 0x229c + - "⊝": [T: "traço circulado"] # 0x229d + - "⊞": [T: "mais enquadrado"] # 0x229e + - "⊟": [T: "menos enquadrado"] # 0x229f + - "⊠": [T: "vezes enquadrado"] # 0x22a0 + - "⊡": [T: "ponto enquadrado"] # 0x22a1 + - "⊢": [T: "demonstra"] # 0x22a2 + - "⊣": [T: "não produz"] # 0x22a3 + - "⊤": [T: "topo"] # 0x22a4 + - "⊦": [T: "reduz a"] # 0x22a6 + - "⊧": [T: "modela"] # 0x22a7 + - "⊨": # 0x22a8 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "verdadeiro" + - "⊩": [T: "força"] # 0x22a9 + - "⊪": [T: "barra vertical tripla com catraca à direita"] # 0x22aa + - "⊫": [T: "barra vertical dupla com catraca dupla à direita"] # 0x22ab + - "⊬": [T: "não demonstra"] # 0x22ac + - "⊭": # 0x22ad + # audit-ignore: em português a negação já é frase completa; o "is" do inglês não tem par que valha suprimir no Terse + - T: "não é verdadeiro" + - "⊮": [T: "não força"] # 0x22ae + - "⊯": [T: "barra vertical dupla com catraca dupla à direita negada"] # 0x22af + - "⊰": [T: "precede sob relação"] # 0x22b0 + - "⊱": [T: "sucede sob relação"] # 0x22b1 + - "⊲": # 0x22b2 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "subgrupo normal de" + - "⊳": [T: "contém como subgrupo normal"] # 0x22b3 + - "⊴": # 0x22b4 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "subgrupo normal de ou igual a" + - "⊵": [T: "contém como subgrupo normal ou é igual a"] # 0x22b5 + - "⊶": # 0x22b6 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "imagem original" + - "⊷": # 0x22b7 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "imagem" + - "⊸": [T: "multimapa"] # 0x22b8 + - "⊹": [T: "matriz conjugada hermitiana"] # 0x22b9 + - "⊺": [T: "intercala"] # 0x22ba + - "⊻": [T: "ou exclusivo"] # 0x22bb + - "⊼": [T: "não-e"] # 0x22bc + - "⊽": [T: "não-ou"] # 0x22bd + - "⊾": [T: "ângulo reto com arco"] # 0x22be + - "⊿": [T: "triângulo retângulo"] # 0x22bf + - "⋀": [T: "e lógico"] # 0x22c0 + - "⋁": [T: "ou lógico"] # 0x22c1 + - "⋂": [T: "interseção"] # 0x22c2 + - "⋃": [T: "união"] # 0x22c3 + - "⋄": [T: "operador losango"] # 0x22c4 + - "⋅": # 0x22c5 + - test: + if: "@data-chem-formula-op" + then: [T: "ponto"] + else: [T: "vezes"] + - "⋆": [T: "vezes"] # 0x22c6 + - "⋇": [T: "divisão vezes"] # 0x22c7 + - "⋈": [T: "junção"] # 0x22c8 + - "⋉": # 0x22c9 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "produto semidireto à esquerda" + - "⋊": # 0x22ca + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "produto semidireto à direita" + - "⋋": # 0x22cb + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "junção semidireta à esquerda" + - "⋌": # 0x22cc + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "junção semidireta à direita" + - "⋍": [T: "til invertido igual"] # 0x22cd + - "⋎": [T: "ou lógico ondulado"] # 0x22ce + - "⋏": [T: "e lógico ondulado"] # 0x22cf + - "⋐": # 0x22d0 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "contido de forma fechada em" + - "⋑": # 0x22d1 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "contém de forma fechada" + - "⋒": # 0x22d2 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "interseção dupla" + - "⋓": # 0x22d3 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "união dupla" + - "⋔": # 0x22d4 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "transversal a" + - "⋕": # 0x22d5 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "igual e paralelo a" # era "paralelo forquilhado a" (descreve a forquilha); en: 'is equal to and parallel to' + - "⋖": [T: "menor que com ponto"] # 0x22d6 + - "⋗": [T: "maior que com ponto"] # 0x22d7 + - "⋘": # 0x22d8 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "muito menor que" + - "⋙": # 0x22d9 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "muito maior que" + - "⋚": # 0x22da + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "menor que, igual ou maior que" + - "⋛": # 0x22db + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "maior que, igual ou menor que" + - "⋜": # 0x22dc + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "igual ou menor que" + - "⋝": # 0x22dd + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "igual ou maior que" + - "⋞": # 0x22de + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "igual ou precede" + - "⋟": # 0x22df + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "igual ou sucede" + - "⋠": [T: "não precede nem é igual a"] # 0x22e0 + - "⋡": [T: "não sucede nem é igual a"] # 0x22e1 + - "⋢": [T: "não é imagem quadrada de nem igual a"] # 0x22e2 + - "⋣": [T: "não é original quadrado de nem igual a"] # 0x22e3 + - "⋤": [T: "imagem quadrada de ou não igual a"] # 0x22e4 + - "⋥": [T: "original quadrado de ou não igual a"] # 0x22e5 + - "⋦": # 0x22e6 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "menor mas não equivalente a" + - "⋧": # 0x22e7 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "maior mas não equivalente a" + - "⋨": [T: "precede mas não é equivalente a"] # 0x22e8 + - "⋩": [T: "sucede mas não é equivalente a"] # 0x22e9 + - "⋪": # 0x22ea + # audit-ignore: em português a negação já é frase completa; o "is" do inglês não tem par que valha suprimir no Terse + - T: "não é subgrupo normal de" + - "⋫": [T: "não contém como subgrupo normal"] # 0x22eb + - "⋬": # 0x22ec + # audit-ignore: em português a negação já é frase completa; o "is" do inglês não tem par que valha suprimir no Terse + - T: "não é subgrupo normal de nem igual a" + - "⋭": [T: "não contém como subgrupo normal nem é igual a"] # 0x22ed + - "⋮": [T: "reticências verticais"] # 0x22ee + - "⋯": [T: "reticências"] # 0x22ef + - "⋰": [T: "reticências diagonais para cima"] # 0x22f0 + - "⋱": [T: "reticências diagonais"] # 0x22f1 + - "⋲": [T: "pertence a com traço horizontal longo"] # 0x22f2 + - "⋳": [T: "pertence a com barra vertical no fim do traço"] # 0x22f3 + - "⋴": [T: "pertence a com barra vertical no fim do traço"] # 0x22f4 + - "⋵": [T: "pertence a com ponto acima"] # 0x22f5 + - "⋶": [T: "pertence a com barra acima"] # 0x22f6 + - "⋷": [T: "pertence a com barra acima"] # 0x22f7 + - "⋸": [T: "pertence a com barra abaixo"] # 0x22f8 + - "⋹": [T: "pertence a com dois traços horizontais"] # 0x22f9 + - "⋺": [T: "contém com traço horizontal longo"] # 0x22fa + - "⋻": [T: "contém com barra vertical no fim do traço"] # 0x22fb + - "⋼": [T: "contém com barra vertical no fim do traço"] # 0x22fc + - "⋽": [T: "contém com barra acima"] # 0x22fd + - "⋾": [T: "contém com barra acima"] # 0x22fe + - "⋿": [T: "pertinência de multiconjunto da notação z"] # 0x22ff + - "■": [T: "quadrado preto"] # 0x25a0 + - "□": [T: "quadrado branco"] # 0x25a1 + - "▢": [T: "quadrado branco de cantos arredondados"] # 0x25a2 + - "▣": [T: "quadrado branco contendo quadrado preto pequeno"] # 0x25a3 + - "▤": [T: "quadrado com preenchimento horizontal"] # 0x25a4 + - "▥": [T: "quadrado com preenchimento vertical"] # 0x25a5 + - "▦": [T: "quadrado com hachura ortogonal"] # 0x25a6 + - "▧": [T: "quadrado com preenchimento diagonal descendente"] # 0x25a7 + - "▨": [T: "quadrado com preenchimento diagonal ascendente"] # 0x25a8 + - "▩": [T: "quadrado com hachura diagonal"] # 0x25a9 + - "▪": [T: "quadrado preto pequeno"] # 0x25aa + - "▫": [T: "quadrado branco pequeno"] # 0x25ab + - "▬": [T: "retângulo preto"] # 0x25ac + - "▭": [T: "retângulo branco"] # 0x25ad + - "▮": [T: "retângulo preto vertical"] # 0x25ae + - "▯": [T: "retângulo branco vertical"] # 0x25af + - "▰": [T: "paralelogramo preto"] # 0x25b0 + - "▱": [T: "paralelogramo branco"] # 0x25b1 + - "▲": [T: "triângulo preto apontando para cima"] # 0x25b2 + - "△": [T: "triângulo"] # 0x25b3 + - "▴": [T: "triângulo preto pequeno apontando para cima"] # 0x25b4 + - "▵": [T: "triângulo branco pequeno apontando para cima"] # 0x25b5 + - "▶": [T: "triângulo preto apontando para a direita"] # 0x25b6 + - "▷": [T: "triângulo branco apontando para a direita"] # 0x25b7 + - "▸": [T: "triângulo preto pequeno apontando para a direita"] # 0x25b8 + - "▹": [T: "triângulo branco pequeno apontando para a direita"] # 0x25b9 + - "►": [T: "ponteiro preto para a direita"] # 0x25ba + - "▻": [T: "ponteiro branco para a direita"] # 0x25bb + - "▼": [T: "triângulo preto apontando para baixo"] # 0x25bc + - "▽": [T: "triângulo branco apontando para baixo"] # 0x25bd + - "▾": [T: "triângulo preto pequeno apontando para baixo"] # 0x25be + - "▿": [T: "triângulo branco pequeno apontando para baixo"] # 0x25bf + - "◀": [T: "triângulo preto apontando para a esquerda"] # 0x25c0 + - "◁": [T: "triângulo branco apontando para a esquerda"] # 0x25c1 + - "◂": [T: "triângulo preto pequeno apontando para a esquerda"] # 0x25c2 + - "◃": [T: "triângulo branco pequeno apontando para a esquerda"] # 0x25c3 + - "◄": [T: "ponteiro preto para a esquerda"] # 0x25c4 + - "◅": [T: "ponteiro branco para a esquerda"] # 0x25c5 + - "◆": [T: "losango preto"] # 0x25c6 + - "◇": [T: "losango branco"] # 0x25c7 + - "◈": [T: "losango branco contendo losango preto pequeno"] # 0x25c8 + - "◉": [T: "olho de peixe"] # 0x25c9 + - "◊": [T: "losango"] # 0x25ca + - "○": [T: "círculo branco"] # 0x25cb + - "◌": [T: "círculo pontilhado"] # 0x25cc + - "◍": [T: "círculo com preenchimento vertical"] # 0x25cd + - "◎": [T: "alvo"] # 0x25ce + - "●": [T: "círculo preto"] # 0x25cf + - "◐": [T: "círculo com metade esquerda preta"] # 0x25d0 + - "◑": [T: "círculo com metade direita preta"] # 0x25d1 + - "◒": [T: "círculo com metade inferior preta"] # 0x25d2 + - "◓": [T: "círculo com metade superior preta"] # 0x25d3 + - "◔": [T: "círculo com quadrante superior direito preto"] # 0x25d4 + - "◕": [T: "círculo preto exceto o quadrante superior esquerdo"] # 0x25d5 + - "◖": [T: "meio círculo preto esquerdo"] # 0x25d6 + - "◗": [T: "meio círculo preto direito"] # 0x25d7 + - "◘": [T: "marcador inverso"] # 0x25d8 + - "◙": [T: "círculo branco inverso"] # 0x25d9 + - "◚": [T: "metade superior de círculo branco inverso"] # 0x25da + - "◛": [T: "metade inferior de círculo branco inverso"] # 0x25db + - "◜": [T: "arco circular do quadrante superior esquerdo"] # 0x25dc + - "◝": [T: "arco circular do quadrante superior direito"] # 0x25dd + - "◞": [T: "arco circular do quadrante inferior direito"] # 0x25de + - "◟": [T: "arco circular do quadrante inferior esquerdo"] # 0x25df + - "◠": [T: "semicírculo superior"] # 0x25e0 + - "◡": [T: "semicírculo inferior"] # 0x25e1 + - "◢": [T: "triângulo preto inferior direito"] # 0x25e2 + - "◣": [T: "triângulo preto inferior esquerdo"] # 0x25e3 + - "◤": [T: "triângulo preto superior esquerdo"] # 0x25e4 + - "◥": [T: "triângulo preto superior direito"] # 0x25e5 + - "◦": [T: "composição"] # 0x25e6 + - "◧": [T: "quadrado com metade esquerda preta"] # 0x25e7 + - "◨": [T: "quadrado com metade direita preta"] # 0x25e8 + - "◩": [T: "quadrado com metade superior esquerda preta"] # 0x25e9 + - "◪": [T: "quadrado com metade inferior direita preta"] # 0x25ea + - "◫": [T: "quadrado branco com linha divisória"] # 0x25eb + - "◬": [T: "triângulo branco apontando para cima com ponto"] # 0x25ec + - "◭": [T: "triângulo apontando para cima com metade esquerda preta"] # 0x25ed + - "◮": [T: "triângulo apontando para cima com metade direita preta"] # 0x25ee + - "◯": [T: "círculo grande"] # 0x25ef + - "◰": [T: "quadrado branco com quadrante superior esquerdo"] # 0x25f0 + - "◱": [T: "quadrado branco com quadrante inferior esquerdo"] # 0x25f1 + - "◲": [T: "quadrado branco com quadrante inferior direito"] # 0x25f2 + - "◳": [T: "quadrado branco com quadrante superior direito"] # 0x25f3 + - "◴": [T: "círculo branco com quadrante superior esquerdo"] # 0x25f4 + - "◵": [T: "círculo branco com quadrante inferior esquerdo"] # 0x25f5 + - "◶": [T: "círculo branco com quadrante inferior direito"] # 0x25f6 + - "◷": [T: "círculo branco com quadrante superior direito"] # 0x25f7 + - "◸": [T: "triângulo superior esquerdo"] # 0x25f8 + - "◹": [T: "triângulo superior direito"] # 0x25f9 + - "◺": [T: "triângulo inferior esquerdo"] # 0x25fa + - "◻": [T: "quadrado branco médio"] # 0x25fb + - "◼": [T: "quadrado preto médio"] # 0x25fc + - "◽": [T: "quadrado branco pequeno médio"] # 0x25fd + - "◾": [T: "quadrado preto pequeno médio"] # 0x25fe + - "◿": [T: "triângulo inferior direito"] # 0x25ff + + +# Ordem das palavras: ao contrário do inglês, que antepõe o estilo +# ("fraktur cap a"), o português pospõe o adjetivo ("maiúscula a vazado"). +# Por isso o `spell:` vem ANTES da palavra de estilo nos blocos de alfabeto. +# Isso segue a expectativa já registrada em tests/Languages/pt/alphabets.rs +# ("maiúscula delta vazado"). + + +# ---- Frações vulgares (0x00bc-0x00be, 0x2150-0x215f) ---- + - "¼": [T: "um quarto"] # 0x00bc + - "½": [T: "um meio"] # 0x00bd + - "¾": [T: "três quartos"] # 0x00be + - "⅐": [T: "um sétimo"] # 0x2150 + - "⅑": [T: "um nono"] # 0x2151 + - "⅒": [T: "um décimo"] # 0x2152 + - "⅓": [T: "um terço"] # 0x2153 + - "⅔": [T: "dois terços"] # 0x2154 + - "⅕": [T: "um quinto"] # 0x2155 + - "⅖": [T: "dois quintos"] # 0x2156 + - "⅗": [T: "três quintos"] # 0x2157 + - "⅘": [T: "quatro quintos"] # 0x2158 + - "⅙": [T: "um sexto"] # 0x2159 + - "⅚": [T: "cinco sextos"] # 0x215a + - "⅛": [T: "um oitavo"] # 0x215b + - "⅜": [T: "três oitavos"] # 0x215c + - "⅝": [T: "cinco oitavos"] # 0x215d + - "⅞": [T: "sete oitavos"] # 0x215e + - "⅟": [T: "um sobre"] # 0x215f + +# ---- Sobrescritos e subscritos (0x2070-0x209f) ---- + - "⁰": [T: "elevado a zero"] # 0x2070 + - "ⁱ": [T: "elevado a i"] # 0x2071 + - "⁴": [T: "elevado à quarta potência"] # 0x2074 + - "⁵": [T: "elevado à quinta potência"] # 0x2075 + - "⁶": [T: "elevado à sexta potência"] # 0x2076 + - "⁷": [T: "elevado à sétima potência"] # 0x2077 + - "⁸": [T: "elevado à oitava potência"] # 0x2078 + - "⁹": [T: "elevado à nona potência"] # 0x2079 + - "⁺": [T: "sinal de mais sobrescrito"] # 0x207a + - "⁻": [T: "menos sobrescrito"] # 0x207b + - "⁼": [T: "sinal de igual sobrescrito"] # 0x207c + - "⁽": [T: "abre parênteses sobrescrito"] # 0x207d + - "⁾": [T: "fecha parênteses sobrescrito"] # 0x207e + - "ⁿ": [T: "elevado à enésima potência"] # 0x207f + - "₀": [T: "subscrito zero"] # 0x2080 + - "₁": [T: "subscrito um"] # 0x2081 + - "₂": [T: "subscrito dois"] # 0x2082 + - "₃": [T: "subscrito três"] # 0x2083 + - "₄": [T: "subscrito quatro"] # 0x2084 + - "₅": [T: "subscrito cinco"] # 0x2085 + - "₆": [T: "subscrito seis"] # 0x2086 + - "₇": [T: "subscrito sete"] # 0x2087 + - "₈": [T: "subscrito oito"] # 0x2088 + - "₉": [T: "subscrito nove"] # 0x2089 + - "₊": [T: "sinal de mais subscrito"] # 0x208a + - "₋": [T: "sinal de menos subscrito"] # 0x208b + - "₌": [T: "sinal de igual subscrito"] # 0x208c + - "₍": [T: "abre parênteses subscrito"] # 0x208d + - "₎": [T: "fecha parênteses subscrito"] # 0x208e + - "ₐ": [T: "subscrito A"] # 0x2090 + - "ₑ": [T: "subscrito E"] # 0x2091 + - "ₒ": [T: "subscrito O"] # 0x2092 + - "ₓ": [T: "subscrito X"] # 0x2093 + - "ₕ": [T: "subscrito H"] # 0x2095 + - "ₖ": [T: "subscrito K"] # 0x2096 + - "ₗ": [T: "subscrito L"] # 0x2097 + - "ₘ": [T: "subscrito M"] # 0x2098 + - "ₙ": [T: "subscrito N"] # 0x2099 + - "ₚ": [T: "subscrito P"] # 0x209a + - "ₛ": [T: "subscrito S"] # 0x209b + - "ₜ": [T: "subscrito T"] # 0x209c + +# ---- Numerais romanos (0x2160-0x217f) ---- + + - "Ⅰ": [T: "um"] # 0x2160 (valor 1) + - "Ⅱ": [T: "dois"] # 0x2161 (valor 2) + - "Ⅲ": [T: "três"] # 0x2162 (valor 3) + - "Ⅳ": [T: "quatro"] # 0x2163 (valor 4) + - "Ⅴ": [T: "cinco"] # 0x2164 (valor 5) + - "Ⅵ": [T: "seis"] # 0x2165 (valor 6) + - "Ⅶ": [T: "sete"] # 0x2166 (valor 7) + - "Ⅷ": [T: "oito"] # 0x2167 (valor 8) + - "Ⅸ": [T: "nove"] # 0x2168 (valor 9) + - "Ⅹ": [T: "dez"] # 0x2169 (valor 10) + - "Ⅺ": [T: "onze"] # 0x216a (valor 11) + - "Ⅻ": [T: "doze"] # 0x216b (valor 12) + - "Ⅼ": [T: "cinquenta"] # 0x216c (valor 50) + - "Ⅽ": [T: "cem"] # 0x216d (valor 100) + - "Ⅾ": [T: "quinhentos"] # 0x216e (valor 500) + - "Ⅿ": [T: "mil"] # 0x216f (valor 1000) + - "ⅰ": [T: "um"] # 0x2170 (valor 1) + - "ⅱ": [T: "dois"] # 0x2171 (valor 2) + - "ⅲ": [T: "três"] # 0x2172 (valor 3) + - "ⅳ": [T: "quatro"] # 0x2173 (valor 4) + - "ⅴ": [T: "cinco"] # 0x2174 (valor 5) + - "ⅵ": [T: "seis"] # 0x2175 (valor 6) + - "ⅶ": [T: "sete"] # 0x2176 (valor 7) + - "ⅷ": [T: "oito"] # 0x2177 (valor 8) + - "ⅸ": [T: "nove"] # 0x2178 (valor 9) + - "ⅹ": [T: "dez"] # 0x2179 (valor 10) + - "ⅺ": [T: "onze"] # 0x217a (valor 11) + - "ⅻ": [T: "doze"] # 0x217b (valor 12) + - "ⅼ": [T: "cinquenta"] # 0x217c (valor 50) + - "ⅽ": [T: "cem"] # 0x217d (valor 100) + - "ⅾ": [T: "quinhentos"] # 0x217e (valor 500) + - "ⅿ": [T: "mil"] # 0x217f (valor 1000) + +# ---- Alfabetos matemáticos: fraktur, vazado (double-struck) e caligráfico ---- +# Símbolos avulsos do bloco "letterlike" (0x2100-0x214f). + + - "ℊ": [T: "g caligráfico"] # 0x210a + - "ℯ": [T: "e caligráfico"] # 0x212f + - "ℴ": [T: "o caligráfico"] # 0x2134 + - "ℼ": [T: "pi vazado"] # 0x213c + - "ℽ": [T: "gama vazado"] # 0x213d + - "⅀": [T: "somatório n-ário vazado"] # 0x2140 + + - "ℌℑℨℭ": # 0x210c, 0x2111, 0x2128, 0x212d + - spell: "translate('.', 'ℌℑℨℭ', 'HIZC')" + - t: "fraktur" + + - "ℍℙℾℿ": # 0x210d, 0x2119, 0x213e, 0x213f + - spell: "translate('.', 'ℍℙℾℿ', 'HPΓΠ')" + - T: "vazado" + + - "ℐℒ℘ℬℰℱℳ": # 0x2110, 0x2112, 0x2118, 0x2130, 0x2131, 0x2133 + - spell: "translate('.', 'ℐℒ℘ℬℰℱℳ', 'ILPBEFM')" + - t: "caligráfico" + +# Blocos alfabéticos do plano 1 e as cópias na área de uso privado do +# MathType. Chaves e argumentos de translate() copiados byte a byte do en. + + - "𝔄-𝔜": # 0x1d504 - 0x1d51d ('z' version is reserved) + - spell: "translate('.', '𝔄𝔅𝔆𝔇𝔈𝔉𝔊𝔋𝔌𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔕𝔖𝔗𝔘𝔙𝔚𝔛𝔜', 'ABCDEFGHIJKLMNOPQRSTUVWXY')" + - t: "fraktur" + + - "-": # 0xf000 - 0xf018 + - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXY')" + - t: "fraktur" + + - "𝔞-𝔷": # 0x1d51e - 0x1d537 + - spell: "translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz')" + - t: "fraktur" + + - "-": # 0xf01a - 0xf033 + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - t: "fraktur" + + - "𝕬-𝖅": # 0x1D56C - 0x1D585 + - spell: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - t: "fraktur" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + + - "-": # 0xf040 - 0xf059 + - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - t: "fraktur" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + + - "𝖆-𝖟": # 0x1d586 - 0x1d59f + - spell: "translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz')" + - t: "fraktur" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + + - "-": # 0xf05a - 0xf073 + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - t: "fraktur" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + + - "𝔸-𝕐": # 0x1d504 - 0x1d51d ('z' version is reserved) + - spell: "translate('.', '𝔸𝔹𝔺𝔻𝔼𝔽𝔾𝔿𝕀𝕁𝕂𝕃𝕄𝕅𝕆𝕇𝕈𝕉𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - T: "vazado" + + - "-": # 0xf080 - 0xf098 + - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - T: "vazado" + + - "𝕒-𝕫": # 0x1d552 - 0x1d56b + - spell: "translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz')" + - T: "vazado" + + - "-": # 0xf09a - 0xf0b3 + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - T: "vazado" + + - "𝟘-𝟡": # 0x1d7d8 - 0x1d7e1 + - spell: "translate('.', '𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡', '0123456789')" + - T: "vazado" + + - "-": # 0xf0c0 - 0xf0c9 + - spell: "translate('.', '', '0123456789')" + - T: "vazado" + + - "𝒜-𝒵": # 0x1d49c - 0x1d4b5 + - spell: "translate('.', '𝒜𝒝𝒞𝒟𝒠𝒡𝒢𝒣𝒤𝒥𝒦𝒧𝒨𝒩𝒪𝒫𝒬𝒭𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - t: "caligráfico" + + - "-": # 0xf100 - 0xf119 + - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - t: "caligráfico" + + - "𝒶-𝓏": # 0x1d4b6 - 0x1d4cf + - spell: "translate('.', '𝒶𝒷𝒸𝒹𝒺𝒻𝒼𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝓄𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏', 'abcdefghijklmnopqrstuvwxyz')" + - t: "caligráfico" + + - "-": # 0xf11a - 0xf133 + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - t: "caligráfico" + + - "𝓐-𝓩": # 0x1d4d0 - 0x1d4e9 + - spell: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - t: "caligráfico" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + + - "-": # 0xf140 - 0xf159 + - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - t: "caligráfico" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + + - "𝓪-𝔃": # 0x1d4ea - 0x1d503 + - spell: "translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz')" + - t: "caligráfico" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + + - "-": # 0xf15a - 0xf173 + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - t: "caligráfico" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + + - "": [T: "nabla vazado"] # 0xf0ca + - "": [T: "constante de Euler vazada"] # 0xf0cb + +# ---- Rodada 9: caracteres que o corpus real (latex2mathml do ACESSÍLIA) mostrou +# saindo CRUS para o sintetizador. Prioridade pelo dano ao estudante, não pelo +# tamanho da lacuna. Ver ACHADOS 9 e TERMINOLOGIA_PT_BR.md. +# +# Letras negrito: \mathbf{x} (vetores em Álgebra Linear) e \boldsymbol{\alpha}. +# Ordem posposta (8.1): "x negrito". Grego negrito passa de novo pela tabela +# e sai com o nome da letra ("alfa negrito"). + - "𝐀-𝐙": # 0x1d400 - 0x1d419 \mathbf maiúsculas + - spell: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - "𝐚-𝐳": # 0x1d41a - 0x1d433 \mathbf minúsculas + - spell: "translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz')" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - "𝑨-𝒁": # 0x1d468 - 0x1d481 negrito itálico maiúsculas (o en também fala só "bold") + - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - "𝒂-𝒛": # 0x1d482 - 0x1d49b negrito itálico minúsculas + - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 grego negrito maiúsculas + - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - "𝛂-𝛚": # 0x1d6c2 - 0x1d6da grego negrito minúsculas + - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒𝛓𝛔𝛕𝛖𝛗𝛘𝛙𝛚', 'αβγδεζηθικλμνξοπρςστυφχψω')" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - "𝜜-𝜴": # 0x1d71c - 0x1d734 grego negrito itálico maiúsculas (\boldsymbol) + - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳𝜴', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - "𝜶-𝝎": # 0x1d736 - 0x1d74e grego negrito itálico minúsculas (\boldsymbol) + - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆𝝇𝝈𝝉𝝊𝝋𝝌𝝍𝝎', 'αβγδεζηθικλμνξοπρςστυφχψω')" + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + +# Produto interno ⟨u, v⟩ e as setas longas (reações químicas, implicação). + - "⟨": [T: "abre colchete angular"] # 0x27e8 (en: 'left angle bracket') + - "⟩": [T: "fecha colchete angular"] # 0x27e9 + - "⟪": [T: "abre colchete angular duplo"] # 0x27ea + - "⟫": [T: "fecha colchete angular duplo"] # 0x27eb + - "⟵": [T: "seta longa para a esquerda"] # 0x27f5 + - "⟶": [T: "seta longa para a direita"] # 0x27f6 (em química a regra de reação fala "reage formando" antes de chegar aqui) + - "⟷": [T: "seta longa para a esquerda e para a direita"] # 0x27f7 + - "⟸": [T: "seta dupla longa para a esquerda"] # 0x27f8 + - "⟹": [T: "implica"] # 0x27f9 (en: 'implies') + - "⟺": [T: "se e somente se"] # 0x27fa (en: 'if and only if') + +# Barra sobre variável: \bar{x}, \overline{x}. O motor canonicaliza U+0304 e +# U+203E para U+00AF, então a entrada "̄" (com mácron) do unicode.yaml nunca +# era alcançada em . "x barra" é a leitura corrente (média amostral). + - "¯": # 0xaf + - test: + if: "ancestor::m:modified-variable and preceding-sibling::*[1][self::m:mi]" + then: [T: "barra"] # (en: 'bar') + else: [T: "linha superior"] # (en: 'line') + +# Sinais "letterlike" que aparecem soltos (fora de unidade, onde definitions.yaml +# já resolve): a leitura é a da letra, não a do prefixo. + - "µ": [T: "mi"] # 0xb5 MICRO SIGN — como letra; "micro" só dentro de unidade + - "Ω": [T: "ômega"] # 0x2126 OHM SIGN — espaço amostral, etc. + - "℧": [T: "mho"] # 0x2127 + - "Å": [T: "angstrom"] # 0xc5 + - "Å": [T: "angstrom"] # 0x212b + + + - "¤": [T: "símbolo de moeda"] + - "¥": [T: "iene"] + - "¦": [T: "barra quebrada"] + - "§": [T: "seção"] + - "©": [T: "direitos autorais"] + - "ª": [T: "feminino ordinal indicador"] + - "«": [T: "apontando para a esquerda duplo aspas angulares"] + - "²": [T: "dois"] + - "³": [T: "três"] + - "¹": [T: "um"] + - "º": [T: "masculino ordinal indicador"] + - "¡": [T: "ponto invertido de exclamação"] + - "¶": [T: "parágrafo marca"] + - "¿": [T: "ponto invertido de interrogação"] + - "ʰ": [T: "modificador pequeno agá"] + - "ʱ": [T: "modificador pequeno agá com gancho"] + - "ʲ": [T: "modificador pequeno jota"] + - "ʳ": [T: "modificador pequeno erre"] + - "ʴ": [T: "modificador pequeno invertido erre"] + - "ʵ": [T: "modificador pequeno invertido erre com gancho"] + - "ʶ": + - spell: "translate('R', 'R', 'R')" + - T: "modificador pequeno invertido" + - "ʷ": [T: "modificador pequeno w"] + - "ʸ": [T: "modificador pequeno y"] + - "ʹ": [T: "modificador linha"] + - "ʺ": [T: "modificador duplo linha"] + - "ʻ": [T: "modificador invertido vírgula"] + - "ʼ": [T: "modificador apóstrofo"] + - "ʽ": [T: "modificador invertido vírgula"] + - "ʾ": [T: "modificador direita metade anel"] + - "ʿ": [T: "modificador esquerda metade anel"] + - "ˀ": [T: "modificador oclusiva glotal"] + - "ˁ": [T: "modificador invertido oclusiva glotal"] + - "˂": [T: "modificador esquerda ponta de seta"] + - "˃": [T: "modificador direita ponta de seta"] + - "˄": [T: "modificador cima ponta de seta"] + - "˅": [T: "modificador baixo ponta de seta"] + - "ˆ": [T: "modificador circunflexo acento"] + - "ˇ": [T: "cáron"] + - "ˈ": [T: "modificador barra vertical"] + - "ˉ": [T: "modificador mácron"] + - "ˊ": [T: "modificador agudo acento"] + - "ˋ": [T: "modificador grave acento"] + - "ˌ": [T: "modificador baixo barra vertical"] + - "ˍ": [T: "modificador baixo mácron"] + - "ˎ": [T: "modificador baixo grave acento"] + - "ˏ": [T: "modificador baixo agudo acento"] + - "ː": [T: "modificador triangular dois pontos"] + - "ˑ": [T: "modificador metade triangular dois pontos"] + - "˒": [T: "modificador centralizado direita metade anel"] + - "˓": [T: "modificador centralizado esquerda metade anel"] + - "˔": [T: "modificador cima taco"] + - "˕": [T: "modificador baixo taco"] + - "˖": [T: "modificador sinal de mais"] + - "˗": [T: "modificador sinal de menos"] + - "˞": [T: "modificador rótico gancho"] + - "˟": [T: "cruzes modificador acento"] + - "ˠ": [T: "modificador pequeno gama"] + - "ˡ": [T: "modificador pequeno l"] + - "ˢ": [T: "modificador pequeno esse"] + - "ˣ": [T: "modificador pequeno xis"] + - "ˤ": [T: "modificador pequeno invertido oclusiva glotal"] + - "˥": [T: "modificador extra-alto tom barra"] + - "˦": [T: "modificador alto tom barra"] + - "˧": [T: "modificador médio tom barra"] + - "˨": [T: "modificador baixo tom barra"] + - "˩": [T: "modificador extra-baixo tom barra"] + - "˪": [T: "modificador yin partindo tom marca"] + - "˫": [T: "modificador yang partindo tom marca"] + - "ˬ": [T: "modificador sonorização"] + - "˭": [T: "modificador não aspirado"] + - "ˮ": [T: "modificador duplo apóstrofo"] + - "˯": [T: "modificador baixo baixo ponta de seta"] + - "˰": [T: "modificador baixo cima ponta de seta"] + - "˱": [T: "modificador baixo esquerda ponta de seta"] + - "˲": [T: "modificador baixo direita ponta de seta"] + - "˳": [T: "modificador baixo anel"] + - "˴": [T: "modificador do meio grave acento"] + - "˵": [T: "modificador do meio duplo grave acento"] + - "˶": [T: "modificador do meio duplo agudo acento"] + - "˷": [T: "modificador baixo til"] + - "˸": [T: "modificador elevado dois pontos"] + - "˹": [T: "modificador início alto tom"] + - "˺": [T: "modificador fim alto tom"] + - "˻": [T: "modificador início baixo tom"] + - "˼": [T: "modificador fim baixo tom"] + - "˽": [T: "modificador prateleira"] + - "˾": [T: "modificador aberto prateleira"] + - "˿": [T: "seta modificador baixo para a esquerda"] + - "̉": [T: "gancho acima de"] + - "̊": [T: "anel acima de"] + - "̋": [T: "duplo agudo acento"] + - "̍": [T: "barra vertical acima de"] + - "̎": [T: "barra dupla vertical acima de"] + - "̏": [T: "duplo grave acento"] + - "̐": [T: "candrabindu"] + - "̑": [T: "invertido breve"] + - "̒": [T: "invertido vírgula acima de"] + - "̓": [T: "vírgula acima de"] + - "̔": [T: "invertido vírgula acima de"] + - "̕": [T: "vírgula acima de direita"] + - "̖": [T: "grave acento abaixo de"] + - "̗": [T: "agudo acento abaixo de"] + - "̘": [T: "taco abaixo de à esquerda"] + - "̙": [T: "taco abaixo de à direita"] + - "̚": [T: "ângulo acima de à esquerda"] + - "̛": [T: "chifre"] + - "̜": [T: "esquerda metade anel abaixo de"] + - "̝": [T: "taco abaixo de para cima"] + - "̞": [T: "taco abaixo de para baixo"] + - "̟": [T: "sinal de mais abaixo de"] + - "̠": [T: "sinal de menos abaixo de"] + - "̡": [T: "palatalizado gancho abaixo de"] + - "̢": [T: "retroflexo gancho abaixo de"] + - "̣": [T: "ponto abaixo de"] + - "̤": [T: "trema abaixo de"] + - "̥": [T: "anel abaixo de"] + - "̦": [T: "vírgula abaixo de"] + - "̧": [T: "cedilha"] + - "̨": [T: "ogonek"] + - "̩": [T: "barra vertical abaixo de"] + - "̪": [T: "ponte abaixo de"] + - "̫": [T: "arco invertido duplo abaixo de"] + - "̬": [T: "cáron abaixo de"] + - "̭": [T: "circunflexo acento abaixo de"] + - "̮": [T: "breve abaixo de"] + - "̯": [T: "invertido breve abaixo de"] + - "̰": [T: "til abaixo de"] + - "̱": [T: "mácron abaixo de"] + - "̲": [T: "linha baixa"] + - "̳": [T: "linha dupla baixa"] + - "̴": [T: "til sobreposto"] + - "̵": [T: "traço curto sobreposto"] + - "̶": [T: "traço longo sobreposto"] + - "̷": [T: "barra curta sobreposto"] + - "̸": [T: "barra longa sobreposto"] + - "̹": [T: "direita metade anel abaixo de"] + - "̺": [T: "ponte invertida abaixo de"] + - "̻": [T: "quadrado abaixo de"] + - "̼": [T: "gaivota abaixo de"] + - "̽": [T: "xis acima de"] + - "̾": [T: "til vertical"] + - "̿": [T: "linha dupla superior"] + - "̀": [T: "grave tom marca"] + - "́": [T: "agudo tom marca"] + - "͆": [T: "ponte acima de"] + - "ΪΫϏ": + - 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: "maiúscula"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + replace: [spell: "translate('.', 'ΪΫϏ', 'ιυϗ')"] + - T: "com trema" + - "ϊ": [T: "iota com trema"] + - "ϋ": [T: "ípsilon com trema"] + - "ό": [T: "ômicron com tônico"] + - "ύ": [T: "ípsilon com tônico"] + - "ώ": [T: "ômega com tônico"] + - "ϐ": [T: "beta"] + - "ϑ": [T: "teta"] + - "ϒ": [T: "ípsilon com gancho"] + - "ϓ": [T: "ípsilon com agudo e gancho"] + - "ϔ": [T: "ípsilon com trema e gancho"] + - "ϗ": [T: "kai"] + - "Ϙ": [T: "maiúscula arcaico copa"] + - "ϙ": [T: "arcaico copa"] + - "А-Я": + - 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: "maiúscula"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + replace: [spell: "translate('.', 'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ', 'абвгдежзийклмнопрстуфхцчшщъыьэюя')"] + - "а": [T: "a"] + - "б": [T: "ser"] + - "в": [T: "vê"] + - "г": [T: "guê"] + - "д": [T: "de"] + - "е": [T: "iê"] + - "ж": [T: "jê"] + - "з": [T: "zê"] + - "и": [T: "i"] + - "й": [T: "curto i"] + - "к": [T: "ka"] + - "л": [T: "ele"] + - "м": [T: "em"] + - "н": [T: "en"] + - "о": [T: "o"] + - "п": [T: "pê"] + - "р": [T: "erre"] + - "с": [T: "esse"] + - "т": [T: "tê"] + - "у": [T: "u"] + - "ф": [T: "efe"] + - "х": [T: "rrá"] + - "ц": [T: "tsê"] + - "ч": [T: "tchê"] + - "ш": [T: "chá"] + - "щ": [T: "chtchá"] + - "ъ": [T: "forte sinal"] + - "ы": [T: "ierí"] + - "ь": [T: "sinal suave"] + - "э": [T: "e"] + - "ю": [T: "iu"] + - "я": [T: "iá"] + - "؆": [T: "arábico-índico raiz cúbica"] + - "؇": [T: "arábico-índico raiz quarta"] + - "؈": [T: "arábico raio"] + - "‐": [T: "hífen"] + - "‑": [T: "hífen"] + - "‒": [T: "algarismo travessão"] + - "―": [T: "barra horizontal"] + - "†": [T: "adaga"] + - "‡": [T: "duplo adaga"] + - " - ": [t: " "] # RECUO-EN + - "•": + - test: + if: "@data-chem-formula-op" + then: [T: "ponto"] + else: [T: "marcador"] + - "‰": [T: "por mil"] + - "‱": [T: "por dez mil"] + - "‸": [T: "a o"] + - "‹": [T: "simples esquerda apontando aspas angulares"] + - "›": [T: "simples direita apontando aspas angulares"] + - "‼": + - test: + if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" + then: [T: "ponto duplo de exclamação"] # 0x203c + else: [T: "duplo fatorial"] # 0x203c + - "⁄": + - test: + if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" + then: [T: "barra grande"] # 0x203c + else: [T: "dividido por"] # 0x203c + - "⁅": [T: "quadrado esquerda colchete com pena"] + - "⁆": [T: "quadrado direita colchete com pena"] + - "※": [T: "marca de referência"] + - "‿": [T: "sob laço"] + - "⁀": [T: "laço"] + - "⁎": [T: "baixo asterisco"] + - "⁏": [T: "ponto invertido e vírgula"] + - "⁐": [T: "fechamento"] + - "⁑": [T: "dois vertical asteriscos"] + - "⁒": [T: "comercial sinal de menos"] + - "⁗": [T: "linha quádrupla"] + - "⁠": [T: ""] + - "₠": [T: "europeia moeda unidades"] + - "₡": [T: "dois pontos"] + - "₢": [T: "cruzeiro"] + - "₣": [T: "franco"] + - "₤": [T: "lira"] + - "₥": [T: "mills"] + - "₦": [T: "naira"] + - "₧": [T: "peseta"] + - "₨": [T: "rupias"] + - "₩": [T: "won"] + - "₪": [T: "novos shequels"] + - "₫": [T: "dong"] + - "€": [T: "euros"] + - "₭": [T: "kip"] + - "₮": [T: "tugrik"] + - "₯": [T: "dracma"] + - "₰": [T: "pfennigs"] + - "₱": [T: "pesos"] + - "₲": [T: "guaranis"] + - "₳": [T: "australes"] + - "₴": [T: "hryvnias"] + - "₵": [T: "cedis"] + - "₶": [T: "livre tournois"] + - "₷": [T: "spesmilos"] + - "₸": [T: "tenges"] + - "₹": [T: "indiano rupias"] + - "₺": [T: "liras turcas"] + - "⃐": [T: "arpão para a esquerda acima de"] + - "⃑": [T: "arpão para a direita acima de"] + - "⃒": [T: "barra longa vertical sobreposto"] + - "⃓": [T: "barra curta vertical sobreposto"] + - "⃔": [T: "seta no sentido anti-horário acima de"] + - "⃕": [T: "seta no sentido horário acima de"] + - "⃖": [T: "seta para a esquerda acima de"] + - "⃗": [T: "seta para a direita acima de"] + - "⃘": [T: "anel sobreposto"] + - "⃙": [T: "no sentido horário anel sobreposto"] + - "⃚": [T: "no sentido anti-horário anel sobreposto"] + - "⃝": [T: "círculo envolvente"] + - "⃞": [T: "quadrado envolvente"] + - "⃟": [T: "losango envolvente"] + - "⃠": [T: "círculo envolvente barra invertida"] + - "⃡": [T: "seta para a esquerda e para a direita acima de"] + - "⃢": [T: "envolvente tela"] + - "⃣": [T: "envolvente tecla"] + - "⃤": [T: "triângulo envolvente para cima apontando"] + - "⃥": [T: "barra invertida sobreposto"] + - "⃦": [T: "traço duplo vertical"] + - "⃧": [T: "anuidade símbolo"] + - "⃨": [T: "ponto triplo abaixo"] + - "⃩": [T: "ponte larga acima de"] + - "⃪": [T: "seta para a esquerda sobreposto"] + - "⃫": [T: "barra longa dupla sobreposto"] + - "⃬": [T: "arpão para a direita com farpa baixo"] + - "⃭": [T: "arpão para a esquerda com farpa baixo"] + - "⃮": [T: "seta para a esquerda abaixo de"] + - "⃯": [T: "seta para a direita abaixo de"] + - "⃰": [T: "asterisco acima de"] + - "℄": [T: "centro linha símbolo"] + - "℅": [T: "aos cuidados de"] + - "℆": [T: "cada uma"] + - "ℇ": [T: "euler'esse constante"] + - "℈": [T: "escrúpulos"] + - "ℏ": [T: "agá barra"] + - "℔": [T: "libras"] + - "№": [T: "número"] + - "℥": [T: "onças"] + - "℩": [T: "invertido iota"] + - "Ⅎ℺⅁⅂⅃⅄": + - test: + - if: "'.' = '℺'" + then: [T: "rotacionado"] + - else_if: "'.' = 'Ⅎ'" + then: [T: "invertido"] + - else_if: "'.' = '⅃'" + then: [T: "invertido sem serifa"] + else: [T: "invertido sem serifa"] + - spell: "translate('.', 'Ⅎ℺⅁⅂⅃⅄', 'FQGLLY')" + - "ℵ": [T: "primeiro transfinito cardinal"] + - "ℶ": [T: "segundo transfinito cardinal"] + - "ℷ": [T: "terceiro transfinito cardinal"] + - "ℸ": [T: "quarto transfinito cardinal"] + - "⅋": [T: "invertido e comercial"] + - "⅌": [T: "por"] + - "ⅎ": [T: "invertido efe"] + - "↉": [T: "zero terços"] + - "⌀": [T: "diâmetro"] + - "⌁": [T: "seta elétrico"] + - "⌂": [T: "casa"] + - "⌃": [T: "ponta de seta para cima"] + - "⌄": [T: "ponta de seta para baixo"] + - "⌅": [T: "projetivo"] + - "⌆": [T: "perspectiva"] + - "⌇": [T: "linha ondulada"] + - "⌈": [T: "abre teto"] + - "⌉": [T: "fecha teto"] + - "⌊": [T: "abre piso"] + - "⌋": [T: "fecha piso"] + - "⌌": [T: "inferior direita corte"] + - "⌍": [T: "inferior esquerda corte"] + - "⌎": [T: "superior direita corte"] + - "⌏": [T: "superior esquerda corte"] + - "⌐": [T: "invertido não sinal"] + - "⌑": [T: "quadrado losango"] + - "⌒": [T: "arco"] + - "⌓": [T: "segmento"] + - "⌔": [T: "setor"] + - "⌕": [T: "telefone gravador"] + - "⌖": [T: "posição indicador mira"] + - "⌗": [T: "quadrado viewdata"] + - "⌘": [T: "lugar de interesse sinal"] + - "⌙": [T: "invertido não sinal"] + - "⌚": [T: "relógio"] + - "⌛": [T: "ampulheta"] + - "⌜": [T: "superior esquerda canto"] + - "⌝": [T: "superior direita canto"] + - "⌞": [T: "inferior esquerda canto"] + - "⌟": [T: "inferior direita canto"] + - "⌠": [T: "superior metade integral"] + - "⌡": [T: "inferior metade integral"] + - "⌢": [T: "carranca"] + - "⌣": [T: "sorriso"] + - "⌤": [T: "ponta de seta entre dois barras horizontales para cima"] + - "⌥": [T: "tecla opção"] + - "⌦": [T: "apagar à direita"] + - "⌧": [T: "retângulo xis em a caixa"] + - "⌨": [T: "teclado"] + - "〈": [T: "abre colchete apontando ângulo"] + - "〉": [T: "fecha colchete apontando ângulo"] + - "⌫": [T: "apagar à esquerda"] + - "⌬": [T: "benzeno anel"] + - "⌭": [T: "cilindricidade"] + - "⌮": [T: "tudo ao redor perfil"] + - "⌯": [T: "simetria"] + - "⌰": [T: "total excentricidade"] + - "⌱": [T: "dimensão origem"] + - "⌲": [T: "cone de afunilamento"] + - "⌳": [T: "inclinação"] + - "⌴": [T: "rebaixo"] + - "⌵": [T: "escareado"] + - "⌶": [T: "viga i do apl"] + - "⌽": [T: "círculo apl montante"] + - "⌿": [T: "barra do apl com barra sobreposta"] + - "⍰": [T: "desconhecido caixa"] + - "⍼": [T: "seta ângulo com baixo em zigue-zague para a direita"] + - "⎔": [T: "hexágono"] + - "⎕": [T: "caixa"] + - "⎶": [T: "quadrado inferior colchete sobre superior quadrado colchete"] + - "⏜": [T: "parêntese superior"] + - "⏝": [T: "parêntese inferior"] + - "⏞": [T: "chave superior"] + - "⏟": [T: "chave inferior"] + - "⏠": [T: "concha superior de tartaruga colchete"] + - "⏡": [T: "concha inferior de tartaruga colchete"] + - "⏢": [T: "trapézio branco"] + - "⏣": [T: "círculo benzeno direita com"] + - "⏤": [T: "retilineidade"] + - "⏥": [T: "planicidade"] + - "⏦": + - spell: "ac" + - T: "corrente" + - "⏧": [T: "interseção elétrica"] + - "①-⑨": + - spell: "translate('.', '①②③④⑤⑥⑦⑧⑨', '123456789')" + - T: "circulado" + - "⑩": [T: "dez circulado"] + - "⑪": [T: "onze circulado"] + - "⑫": [T: "doze circulado"] + - "⑬": [T: "treze circulado"] + - "⑭": [T: "quatorze circulado"] + - "⑮": [T: "quinze circulado"] + - "⑯": [T: "dezesseis circulado"] + - "⑰": [T: "dezessete circulado"] + - "⑱": [T: "dezoito circulado"] + - "⑳": [T: "vinte circulado"] + - "⑴-⑼": + - spell: "translate('.', '⑴⑵⑶⑷⑸⑹⑺⑻⑼', '123456789')" + - T: "entre parênteses" + - "⑽": [T: "dez entre parênteses"] + - "⑾": [T: "onze entre parênteses"] + - "⑿": [T: "doze entre parênteses"] + - "⒀": [T: "treze entre parênteses"] + - "⒁": [T: "quatorze entre parênteses"] + - "⒂": [T: "quinze entre parênteses"] + - "⒃": [T: "dezesseis entre parênteses"] + - "⒄": [T: "dezessete entre parênteses"] + - "⒅": [T: "dezoito entre parênteses"] + - "⒆": [T: "dezenove entre parênteses"] + - "⒇": [T: "vinte entre parênteses"] + - "⒈-⒐": + - spell: "translate('.', '⒈⒉⒊⒋⒌⒍⒎⒏⒐', '123456789')" + - T: "com ponto" + - "⒑": [T: "dez com ponto final"] + - "⒒": [T: "onze com ponto final"] + - "⒓": [T: "doze com ponto final"] + - "⒔": [T: "treze com ponto final"] + - "⒕": [T: "quatorze com ponto final"] + - "⒖": [T: "quinze com ponto final"] + - "⒗": [T: "dezesseis com ponto final"] + - "⒘": [T: "dezessete com ponto final"] + - "⒙": [T: "dezoito com ponto final"] + - "⒚": [T: "dezenove com ponto final"] + - "⒛": [T: "vinte com ponto final"] + - "⒜-⒵": + - spell: "translate('.', '⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵', 'abcdefghijklmnopqrstuvwxyz')" + - T: "entre parênteses" + - "Ⓐ-ⓩ": + - spell: "translate('.', 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - T: "circulado" + - "🅐-🅩": + - spell: "translate('.', '🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - T: "circulado preto" + - "ⓐ-ⓩ": + - spell: "translate('.', 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ', 'abcdefghijklmnopqrstuvwxyz')" + - T: "circulado" + - "⓪": [T: "zero circulado"] + - "⓫": [T: "onze circulado preto"] + - "⓬": [T: "doze circulado preto"] + - "⓭": [T: "treze circulado preto"] + - "⓮": [T: "quatorze circulado preto"] + - "⓯": [T: "quinze circulado preto"] + - "⓰": [T: "dezesseis circulado preto"] + - "⓱": [T: "dezessete circulado preto"] + - "⓲": [T: "dezoito circulado preto"] + - "⓳": [T: "dezenove circulado preto"] + - "⓴": [T: "vinte circulado preto"] + - "⓵-⓽": + - spell: "translate('.', '⓵⓶⓷⓸⓹⓺⓻⓼⓽', '123456789')" + - T: "duplo circulado" + - "⓾": [T: "dez circulado duas vezes"] + - "⓿": [T: "zero circulado preto"] + - "★": [T: "estrela preta"] + - "☆": [T: "estrela branca"] + - "☉": [T: "sol"] + - "☌": [T: "conjunção"] + - "☒": [T: "cédula caixa com xis"] + - "☽": [T: "crescente lua"] + - "☾": [T: "minguante lua"] + - "☿": [T: "mercúrio"] + - "♀": [T: "feminino"] + - "♁": [T: "terra"] + - "♂": [T: "masculino"] + - "♃": [T: "júpiter"] + - "♄": [T: "saturno"] + - "♅": [T: "urano"] + - "♆": [T: "netuno"] + - "♇": [T: "plutão"] + - "♈": [T: "áries"] + - "♉": [T: "touro"] + - "♩": [T: "um quarto nota"] + - "♭": [T: "bemol"] + - "♮": [T: "natural"] + - "♯": [T: "sustenido"] + - "♠": [T: "preto naipe de espadas"] + - "♡": [T: "branco naipe de copas"] + - "♢": [T: "branco naipe de ouros"] + - "♣": [T: "preto naipe de paus"] + - "♤": [T: "branco naipe de espadas"] + - "♥": [T: "preto naipe de copas"] + - "♦": [T: "preto naipe de ouros"] + - "♧": [T: "branco naipe de paus"] + - "⚀": [T: "face de dado 1"] + - "⚁": [T: "face de dado 2"] + - "⚂": [T: "face de dado 3"] + - "⚃": [T: "face de dado 4"] + - "⚄": [T: "face de dado 5"] + - "⚅": [T: "face de dado 6"] + - "⚆": [T: "círculo branco com ponto direita"] + - "⚇": [T: "círculo branco com dois pontos"] + - "⚈": [T: "círculo preto com ponto direita"] + - "⚉": [T: "círculo preto com dois pontos"] + - "⚪": [T: "círculo médio branco"] + - "⚫": [T: "círculo médio preto"] + - "⚬": [T: "círculo médio pequeno branco"] + - "⚲": [T: "neutro"] + - "✓": [T: "verificação marca"] + - "✠": [T: "cruzes de malta"] + - "✪": [T: "branco estrela circulado"] + - "✶": [T: "estrela seis pontuda preta"] + - "❨": [T: "médio parênteses ornamento à esquerda"] + - "❩": [T: "médio parênteses ornamento à direita"] + - "❪": [T: "médio achatado parênteses ornamento à esquerda"] + - "❫": [T: "médio achatado parênteses ornamento à direita"] + - "❬": [T: "médio apontando para a colchete angular ornamento à esquerda"] + - "❭": [T: "médio apontando para a colchete angular ornamento à direita"] + - "❮": [T: "grosso apontando para a esquerda aspas angulares ornamento"] + - "❯": [T: "grosso apontando para a direita aspas angulares ornamento"] + - "❰": [T: "grosso apontando para a colchete angular ornamento à esquerda"] + - "❱": [T: "grosso apontando para a colchete angular ornamento à direita"] + - "❲": [T: "fino concha de tartaruga colchete ornamento à esquerda"] + - "❳": [T: "fino concha de tartaruga colchete ornamento à direita"] + - "❴": [T: "médio chave ornamento à esquerda"] + - "❵": [T: "médio chave ornamento à direita"] + - "❶": [T: "um circulado preto"] + - "❷": [T: "dois circulado preto"] + - "❸": [T: "três circulado preto"] + - "❹": [T: "quatro circulado preto"] + - "❺": [T: "cinco circulado preto"] + - "❻": [T: "seis circulado preto"] + - "❼": [T: "sete circulado preto"] + - "❽": [T: "oito circulado preto"] + - "❾": [T: "nove circulado preto"] + - "❿": [T: "dez circulado preto"] + - "➀": [T: "um circulado sem serifa"] + - "➁": [T: "dois circulado sem serifa"] + - "➂": [T: "três circulado sem serifa"] + - "➃": [T: "quatro circulado sem serifa"] + - "➄": [T: "cinco circulado sem serifa"] + - "➅": [T: "seis circulado sem serifa"] + - "➆": [T: "sete circulado sem serifa"] + - "➇": [T: "oito circulado sem serifa"] + - "➈": [T: "nove circulado sem serifa"] + - "➉": [T: "dez circulado sem serifa"] + - "➊": [T: "um circulado preto sem serifa"] + - "➋": [T: "dois circulado preto sem serifa"] + - "➌": [T: "três circulado preto sem serifa"] + - "➍": [T: "quatro circulado preto sem serifa"] + - "➎": [T: "cinco circulado preto sem serifa"] + - "➏": [T: "seis circulado preto sem serifa"] + - "➐": [T: "sete circulado preto sem serifa"] + - "➑": [T: "oito circulado preto sem serifa"] + - "➒": [T: "nove circulado preto sem serifa"] + - "➓": [T: "dez circulado preto sem serifa"] + - "➔": [T: "seta grossa de cabeça larga para a direita"] + - "➕": [T: "sinal grosso de mais"] + - "➖": [T: "sinal grosso de menos"] + - "➗": [T: "sinal grosso de divisão"] + - "➘": [T: "seta grossa para sudeste"] + - "➙": [T: "seta grossa para a direita"] + - "➚": [T: "seta grossa para nordeste"] + - "➛": [T: "seta desenho técnico ponto para a direita"] + - "➜": [T: "seta grossa de ponta arredondada para a direita"] + - "➝": [T: "seta de ponta triangular para a direita"] + - "➞": [T: "seta grossa de ponta triangular para a direita"] + - "➟": [T: "seta tracejada de ponta triangular para a direita"] + - "➠": [T: "seta grossa tracejada de ponta triangular para a direita"] + - "➡": [T: "seta preta para a direita"] + - "➢": [T: "seta três dê superior aceso para a direita"] + - "➣": [T: "seta três dê inferior aceso para a direita"] + - "➤": [T: "preto ponta de seta à direita"] + - "➥": [T: "seta grossa preta curva baixo e para a direita"] + - "➦": [T: "seta grossa preta curva cima e para a direita"] + - "➧": [T: "seta achatado preta para a direita"] + - "➨": [T: "seta grossa de ponta côncava preta para a direita"] + - "➩": [T: "seta sombreado à direita branca para a direita"] + - "➪": [T: "seta sombreado à esquerda branca para a direita"] + - "➫": [T: "seta inclinado para trás sombreada branca para a direita"] + - "➬": [T: "seta inclinado para a frente sombreada branca para a direita"] + - "➭": [T: "seta grossa inferior com sombra à direita branca para a direita"] + - "➮": [T: "seta grossa superior com sombra à direita branca para a direita"] + - "➯": [T: "seta entalhada inferior com sombra à direita branca para a direita"] + - "➱": [T: "seta entalhada superior com sombra à direita branca para a direita"] + - "➲": [T: "grosso branco direita seta circulado"] + - "➳": [T: "seta de pena branca para a direita"] + - "➴": [T: "seta de pena preta para sudeste"] + - "➵": [T: "seta de pena preta para a direita"] + - "➶": [T: "seta de pena preta para nordeste"] + - "➷": [T: "seta grossa de pena preta para sudeste"] + - "➸": [T: "seta grossa de pena preta para a direita"] + - "➹": [T: "seta grossa de pena preta para nordeste"] + - "➺": [T: "seta de farpa em gota para a direita"] + - "➻": [T: "seta grossa de haste em gota para a direita"] + - "➼": [T: "seta de cauda em cunha para a direita"] + - "➽": [T: "seta grossa de cauda em cunha para a direita"] + - "➾": [T: "seta de contorno aberto para a direita"] + - "⟀": [T: "três dimensional ângulo"] + - "⟁": [T: "triângulo branco contendo pequeno branco triângulo"] + - "⟃": + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "um aberto contido em" + - "⟄": + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] + - T: "um aberto contém" + - "⟅": [T: "esquerda em forma de s saco delimitador"] + - "⟆": [T: "direita em forma de s saco delimitador"] + - "⟇": [T: "ou com ponto dentro de"] + - "⟈": [T: "barra invertida precedente contido"] + - "⟉": [T: "contendo precedente barra"] + - "⟊": [T: "barra vertical com traço horizontal"] + - "⟋": [T: "matemático subindo diagonal"] + - "⟌": [T: "divisão longa"] + - "⟍": [T: "matemático descendo diagonal"] + - "⟎": [T: "e lógico enquadrado"] + - "⟏": [T: "ou lógico enquadrado"] + - "⟐": [T: "losango branco com centralizado ponto"] + - "⟑": [T: "e com ponto"] + - "⟒": [T: "pertence a abrindo cima"] + - "⟓": [T: "inferior direita canto com ponto"] + - "⟔": [T: "superior esquerda canto com ponto"] + - "⟕": [T: "esquerda externo unir"] + - "⟖": [T: "direita externo unir"] + - "⟗": [T: "completo externo unir"] + - "⟘": [T: "grande taco para cima"] + - "⟙": [T: "grande taco para baixo"] + - "⟚": [T: "esquerda e direita catraca dupla"] + - "⟛": [T: "esquerda e taco à direita"] + - "⟜": [T: "esquerda multimapa"] + - "⟝": [T: "longo taco à direita"] + - "⟞": [T: "longo taco à esquerda"] + - "⟟": [T: "círculo cima taco com acima de"] + - "⟠": [T: "losango dividido por horizontal régua"] + - "⟡": [T: "losango branco côncavo lateral"] + - "⟢": [T: "losango branco côncavo lateral com esquerda marca"] + - "⟣": [T: "losango branco côncavo lateral com direita marca"] + - "⟤": [T: "quadrado branco com esquerda marca"] + - "⟥": [T: "quadrado branco com direita marca"] + - "⟦": [T: "abre colchete branco quadrado"] + - "⟧": [T: "fecha colchete branco quadrado"] + - "⟬": [T: "abre colchete branco concha de tartaruga"] + - "⟭": [T: "fecha colchete branco concha de tartaruga"] + - "⟮": [T: "abre parêntese achatado"] + - "⟯": [T: "fecha parêntese achatado"] + - "⟰": [T: "seta quádrupla para cima"] + - "⟱": [T: "seta quádrupla para baixo"] + - "⟲": [T: "seta no sentido anti-horário com falha círculo"] + - "⟳": [T: "seta no sentido horário com falha círculo"] + - "⟴": [T: "seta para a direita com circulado mais"] + - "⟻": [T: "seta longa para a esquerda a partir de barra"] + - "⟼": [T: "seta longa para a direita a partir de barra"] + - "⟽": [T: "seta longa dupla para a esquerda a partir de barra"] + - "⟾": [T: "seta longa dupla para a direita a partir de barra"] + - "⟿": [T: "seta longa serpenteante para a direita"] + - "⤀": [T: "seta de duas pontas para a direita com vertical traço"] + - "⤁": [T: "seta de duas pontas para a direita com duplo vertical traço"] + - "⤂": [T: "seta dupla para a esquerda com vertical traço"] + - "⤃": [T: "seta dupla para a direita com vertical traço"] + - "⤄": [T: "seta dupla para a esquerda e para a direita com vertical traço"] + - "⤅": [T: "seta de duas pontas para a direita a partir de barra"] + - "⤆": [T: "seta dupla para a esquerda a partir de barra"] + - "⤇": [T: "seta dupla para a direita a partir de barra"] + - "⤈": [T: "seta para baixo com horizontal traço"] + - "⤉": [T: "seta para cima com horizontal traço"] + - "⤊": [T: "seta tripla para cima"] + - "⤋": [T: "seta tripla para baixo"] + - "⤌": [T: "seta dupla travessão para a esquerda"] + - "⤍": [T: "seta dupla travessão para a direita"] + - "⤎": [T: "seta de três traços para a esquerda"] + - "⤏": [T: "seta de três traços para a direita"] + - "⤐": [T: "seta de duas pontas de três traços para a direita"] + - "⤑": [T: "seta para a direita com pontilhado haste"] + - "⤒": [T: "seta para cima a barra"] + - "⤓": [T: "seta para baixo a barra"] + - "⤔": [T: "seta para a direita com cauda e vertical traço"] + - "⤕": [T: "seta para a direita com cauda e duplo vertical traço"] + - "⤖": [T: "seta de duas pontas para a direita com cauda"] + - "⤗": [T: "seta de duas pontas para a direita com cauda com vertical traço"] + - "⤘": [T: "seta de duas pontas para a direita com cauda com duplo vertical traço"] + - "⤙": [T: "seta para a esquerda cauda"] + - "⤚": [T: "seta para a direita cauda"] + - "⤛": [T: "seta dupla para a esquerda cauda"] + - "⤜": [T: "seta dupla para a direita cauda"] + - "⤝": [T: "seta para a esquerda a preenchido losango"] + - "⤞": [T: "seta para a direita a preenchido losango"] + - "⤟": [T: "seta para a esquerda a partir de barra a preenchido losango"] + - "⤠": [T: "seta para a direita a partir de barra a preenchido losango"] + - "⤡": [T: "seta e sul leste para noroeste"] + - "⤢": [T: "seta e sul oeste para nordeste"] + - "⤣": [T: "seta para noroeste com gancho"] + - "⤤": [T: "seta para nordeste com gancho"] + - "⤥": [T: "seta para sudeste com gancho"] + - "⤦": [T: "seta para sudoeste com gancho"] + - "⤧": [T: "seta para noroeste e norte leste seta"] + - "⤨": [T: "seta para nordeste e sul leste seta"] + - "⤩": [T: "seta para sudeste e sul oeste seta"] + - "⤪": [T: "seta para sudoeste e norte oeste seta"] + - "⤫": [T: "subindo diagonal cruzando descendo diagonal"] + - "⤬": [T: "descendo diagonal cruzando subindo diagonal"] + - "⤭": [T: "seta para sudeste cruzando norte leste seta"] + - "⤮": [T: "seta para nordeste cruzando sul leste seta"] + - "⤯": [T: "seta descendo diagonal cruzando para nordeste"] + - "⤰": [T: "seta subindo diagonal cruzando para sudeste"] + - "⤱": [T: "seta para nordeste cruzando norte oeste seta"] + - "⤲": [T: "seta para noroeste cruzando norte leste seta"] + - "⤳": [T: "seta ondulada apontando diretamente direita"] + - "⤴": [T: "seta apontando direita depois curvando cima"] + - "⤵": [T: "seta apontando direita depois curvando baixo"] + - "⤶": [T: "seta apontando baixo depois curvando esquerda"] + - "⤷": [T: "seta apontando baixo depois curvando direita"] + - "⤸": [T: "seta lado arco no sentido horário para a direita"] + - "⤹": [T: "seta lado arco no sentido anti-horário para a esquerda"] + - "⤺": [T: "seta superior arco no sentido anti-horário"] + - "⤻": [T: "seta inferior arco no sentido anti-horário"] + - "⤼": [T: "seta superior arco no sentido horário com menos"] + - "⤽": [T: "seta superior arco no sentido anti-horário com mais"] + - "⤾": [T: "seta inferior semicircular no sentido horário para a direita"] + - "⤿": [T: "seta inferior semicircular no sentido anti-horário para a esquerda"] + - "⥀": [T: "seta no sentido anti-horário fechada círculo"] + - "⥁": [T: "seta no sentido horário fechada círculo"] + - "⥂": [T: "seta para a direita acima de curto esquerda seta"] + - "⥃": [T: "seta para a esquerda acima de curto direita seta"] + - "⥄": [T: "seta curta para a direita acima de esquerda seta"] + - "⥅": [T: "seta para a direita com mais abaixo de"] + - "⥆": [T: "seta para a esquerda com mais abaixo de"] + - "⥇": [T: "seta para a direita atravessado por xis"] + - "⥈": [T: "seta para a esquerda e para a direita atravessado por círculo"] + - "⥉": [T: "seta de duas pontas para cima a partir de círculo"] + - "⥊": [T: "arpão farpa cima direita farpa baixo para a esquerda"] + - "⥋": [T: "arpão farpa baixo direita farpa cima para a esquerda"] + - "⥌": [T: "arpão cima farpa direita baixo farpa para a esquerda"] + - "⥍": [T: "arpão cima farpa baixo farpa direita para a esquerda"] + - "⥎": [T: "arpão farpa cima direita farpa cima para a esquerda"] + - "⥏": [T: "arpão cima farpa baixo farpa direita para a direita"] + - "⥐": [T: "arpão farpa baixo direita farpa baixo para a esquerda"] + - "⥑": [T: "arpão cima farpa baixo farpa esquerda para a esquerda"] + - "⥒": [T: "arpão para a esquerda com farpa cima a barra"] + - "⥓": [T: "arpão para a direita com farpa cima a barra"] + - "⥔": [T: "arpão para cima com farpa direita a barra"] + - "⥕": [T: "arpão para baixo com farpa direita a barra"] + - "⥖": [T: "arpão para a esquerda com farpa baixo a barra"] + - "⥗": [T: "arpão para a direita com farpa baixo a barra"] + - "⥘": [T: "arpão para cima com farpa esquerda a barra"] + - "⥙": [T: "arpão para baixo com farpa esquerda a barra"] + - "⥚": [T: "arpão para a esquerda com farpa cima a partir de barra"] + - "⥛": [T: "arpão para a direita com farpa cima a partir de barra"] + - "⥜": [T: "arpão para cima com farpa direita a partir de barra"] + - "⥝": [T: "arpão para baixo com farpa direita a partir de barra"] + - "⥞": [T: "arpão para a esquerda com farpa baixo a partir de barra"] + - "⥟": [T: "arpão para a direita com farpa baixo a partir de barra"] + - "⥠": [T: "arpão para cima com farpa esquerda a partir de barra"] + - "⥡": [T: "arpão para baixo com farpa esquerda a partir de barra"] + - "⥢": [T: "arpão para a esquerda com farpa cima acima de esquerda arpão com farpa baixo"] + - "⥣": [T: "arpão para cima com farpa esquerda ao lado de cima arpão com farpa direita"] + - "⥤": [T: "arpão para a direita com farpa cima acima de direita arpão com farpa baixo"] + - "⥥": [T: "arpão para baixo com farpa esquerda ao lado de baixo arpão com farpa direita"] + - "⥦": [T: "arpão para a esquerda com farpa cima acima de direita arpão com farpa cima"] + - "⥧": [T: "arpão para a esquerda com farpa baixo acima de direita arpão com farpa baixo"] + - "⥨": [T: "arpão para a direita com farpa cima acima de esquerda arpão com farpa cima"] + - "⥩": [T: "arpão para a direita com farpa baixo acima de esquerda arpão com farpa baixo"] + - "⥪": [T: "arpão para a esquerda com farpa cima acima de longo travessão"] + - "⥫": [T: "arpão para a esquerda com farpa baixo abaixo de longo travessão"] + - "⥬": [T: "arpão para a direita com farpa cima acima de longo travessão"] + - "⥭": [T: "arpão para a direita com farpa baixo abaixo de longo travessão"] + - "⥮": [T: "arpão para cima com farpa esquerda ao lado de baixo arpão com farpa direita"] + - "⥯": [T: "arpão para baixo com farpa esquerda ao lado de cima arpão com farpa direita"] + - "⥰": [T: "seta dupla para a direita com ponta arredondada"] + - "⥱": [T: "seta igual acima de para a direita"] + - "⥲": [T: "seta til operador acima de para a direita"] + - "⥳": [T: "seta para a esquerda acima de operador til"] + - "⥴": [T: "seta para a direita acima de operador til"] + - "⥵": [T: "seta para a direita acima de quase igual a"] + - "⥶": [T: "seta menor que acima de para a esquerda"] + - "⥷": [T: "seta para a esquerda atravessado por menor que"] + - "⥸": [T: "seta maior que acima de para a direita"] + - "⥹": [T: "seta contido acima de para a direita"] + - "⥺": [T: "seta para a esquerda atravessado por contido"] + - "⥻": [T: "seta contendo acima de para a esquerda"] + - "⥼": [T: "esquerda peixe cauda"] + - "⥽": [T: "direita peixe cauda"] + - "⥾": [T: "cima peixe cauda"] + - "⥿": [T: "baixo peixe cauda"] + - "⦀": [T: "barra tripla vertical delimitador"] + - "⦁": [T: "da notação z ponto"] + - "⦂": [T: "da notação z tipo dois pontos"] + - "⦃": [T: "abre chave branca"] + - "⦄": [T: "fecha chave branca"] + - "⦅": [T: "abre parêntese branco"] + - "⦆": [T: "fecha parêntese branco"] + - "⦇": [T: "da notação z esquerda imagem colchete"] + - "⦈": [T: "da notação z direita imagem colchete"] + - "⦉": [T: "da notação z esquerda vinculação colchete"] + - "⦊": [T: "da notação z direita vinculação colchete"] + - "⦋": [T: "quadrado esquerda colchete com barra inferior"] + - "⦌": [T: "quadrado direita colchete com barra inferior"] + - "⦍": [T: "quadrado esquerda colchete com marca em superior canto"] + - "⦎": [T: "quadrado direita colchete com marca em inferior canto"] + - "⦏": [T: "quadrado esquerda colchete com marca em inferior canto"] + - "⦐": [T: "quadrado direita colchete com marca em superior canto"] + - "⦑": [T: "colchete angular com ponto à esquerda"] + - "⦒": [T: "colchete angular com ponto à direita"] + - "⦓": [T: "abre colchete arco menor que"] + - "⦔": [T: "fecha colchete arco maior que"] + - "⦕": [T: "arco duplo esquerda maior que colchete"] + - "⦖": [T: "arco duplo direita menor que colchete"] + - "⦗": [T: "abre colchete preto concha de tartaruga"] + - "⦘": [T: "fecha colchete preto concha de tartaruga"] + - "⦙": [T: "cerca pontilhada"] + - "⦚": [T: "linha vertical em zigue-zague"] + - "⦛": [T: "medido ângulo abrindo esquerda"] + - "⦜": [T: "quadrado direita ângulo variante com"] + - "⦝": [T: "medido ângulo com ponto à direita"] + - "⦞": [T: "ângulo com esse dentro de"] + - "⦟": [T: "agudo ângulo"] + - "⦠": [T: "ângulo esférico abrindo esquerda"] + - "⦡": [T: "ângulo esférico abrindo cima"] + - "⦢": [T: "ângulo invertido"] + - "⦣": [T: "ângulo invertido"] + - "⦤": [T: "ângulo com barra inferior"] + - "⦥": [T: "ângulo invertido com barra inferior"] + - "⦦": [T: "ângulo oblíquo abrindo cima"] + - "⦧": [T: "ângulo oblíquo abrindo baixo"] + - "⦨": [T: "seta medido ângulo com aberta braço terminando em apontando cima e à direita"] + - "⦩": [T: "seta medido ângulo com aberta braço terminando em apontando cima e à esquerda"] + - "⦪": [T: "seta medido ângulo com aberta braço terminando em apontando baixo e à direita"] + - "⦫": [T: "seta medido ângulo com aberta braço terminando em apontando baixo e à esquerda"] + - "⦬": [T: "seta medido ângulo com aberta braço terminando em apontando direita e cima"] + - "⦭": [T: "seta medido ângulo com aberta braço terminando em apontando esquerda e cima"] + - "⦮": [T: "seta medido ângulo com aberta braço terminando em apontando direita e baixo"] + - "⦯": [T: "seta medido ângulo com aberta braço terminando em apontando esquerda e baixo"] + - "⦰": [T: "conjunto invertido vazio"] + - "⦱": [T: "conjunto vazio com barra superior"] + - "⦲": [T: "círculo vazio conjunto com pequeno acima de"] + - "⦳": [T: "seta vazio conjunto com para a direita acima de"] + - "⦴": [T: "seta vazio conjunto com para a esquerda acima de"] + - "⦵": [T: "círculo com barra horizontal"] + - "⦶": [T: "barra vertical circulado"] + - "⦷": [T: "paralelo circulado"] + - "⦸": [T: "invertido barra circulado"] + - "⦹": [T: "perpendicular circulado"] + - "⦺": [T: "dividido por barra horizontal e superior metade dividido por barra vertical circulado"] + - "⦻": [T: "círculo com sobreposto xis"] + - "⦼": [T: "no sentido anti-horário rotacionado sinal de divisão circulado"] + - "⦽": [T: "seta para cima atravessado por círculo"] + - "⦾": [T: "branco marcador circulado"] + - "⦿": [T: "marcador circulado"] + - "⧀": [T: "menor que circulado"] + - "⧁": [T: "maior que circulado"] + - "⧂": [T: "círculo com pequeno círculo à direita"] + - "⧃": [T: "círculo com dois horizontal traços à direita"] + - "⧄": [T: "subindo diagonal barra enquadrado"] + - "⧅": [T: "descendo diagonal barra enquadrado"] + - "⧆": [T: "asterisco enquadrado"] + - "⧇": [T: "pequeno círculo enquadrado"] + - "⧈": [T: "quadrado enquadrado"] + - "⧉": [T: "quadrados dois unido"] + - "⧊": [T: "triângulo com ponto acima de"] + - "⧋": [T: "triângulo com barra inferior"] + - "⧌": [T: "triângulo esse em"] + - "⧍": [T: "triângulo com serifas em inferior"] + - "⧎": [T: "triângulo direita acima de esquerda triângulo"] + - "⧏": [T: "triângulo esquerda ao lado de barra vertical"] + - "⧐": [T: "triângulo vertical barra ao lado de direita"] + - "⧑": [T: "laço com esquerda metade preto"] + - "⧒": [T: "laço com direita metade preto"] + - "⧓": [T: "laço preto"] + - "⧔": [T: "vezes com esquerda metade preto"] + - "⧕": [T: "vezes com direita metade preto"] + - "⧖": [T: "ampulheta branco"] + - "⧗": [T: "ampulheta preto"] + - "⧘": [T: "abre cerca ondulada"] + - "⧙": [T: "fecha cerca ondulada"] + - "⧚": [T: "abre cerca dupla ondulada"] + - "⧛": [T: "fecha cerca dupla ondulada"] + - "⧜": [T: "incompleto infinito"] + - "⧝": [T: "laço sobre infinito"] + - "⧞": [T: "infinito negado com barra vertical"] + - "⧟": [T: "de duas pontas multimapa"] + - "⧠": [T: "quadrado com contornado contorno"] + - "⧡": [T: "aumenta como"] + - "⧢": [T: "embaralhamento produto"] + - "⧣": [T: "sinal de igual e inclinado paralelo"] + - "⧤": [T: "sinal de igual e inclinado paralelo com til acima de"] + - "⧥": [T: "idêntico a e inclinado paralelo"] + - "⧦": [T: "igualmente forte"] + - "⧧": [T: "termodinâmico"] + - "⧨": [T: "triângulo baixo apontando com esquerda metade preto"] + - "⧩": [T: "triângulo baixo apontando com direita metade preto"] + - "⧪": [T: "seta preta losango com para baixo"] + - "⧫": [T: "losango preto"] + - "⧬": [T: "seta branca círculo com para baixo"] + - "⧭": [T: "seta preta círculo com para baixo"] + - "⧮": [T: "quadrado com barra de erro branco"] + - "⧯": [T: "quadrado com barra de erro preto"] + - "⧰": [T: "losango com barra de erro branco"] + - "⧱": [T: "losango com barra de erro preto"] + - "⧲": [T: "círculo com barra de erro branco"] + - "⧳": [T: "círculo com barra de erro preto"] + - "⧴": [T: "regra atrasada"] + - "⧵": [T: "barra invertida operador"] + - "⧶": [T: "barra com barra superior"] + - "⧷": [T: "barra invertida com traço horizontal"] + - "⧸": [T: "barra grande"] + - "⧹": [T: "barra grande invertida"] + - "⧺": [T: "duplo mais"] + - "⧻": [T: "triplo mais"] + - "⧼": [T: "abre colchete apontando curvo ângulo"] + - "⧽": [T: "fecha colchete apontando curvo ângulo"] + - "⧾": [T: "minúsculo"] + - "⧿": [T: "miny"] + - "⨀": [T: "ponto circulado"] + - "⨁": [T: "mais operador circulado"] + - "⨂": [T: "vezes operador circulado"] + - "⨃": [T: "união operador com ponto"] + - "⨄": [T: "união operador com mais"] + - "⨅": [T: "quadrado interseção operador"] + - "⨆": [T: "quadrado união operador"] + - "⨇": [T: "dois e lógico operador"] + - "⨈": [T: "dois ou lógico operador"] + - "⨉": [T: "vezes operador"] + - "⨊": [T: "módulo dois soma"] + - "⨋": [T: "somatório com integral"] + - "⨌": [T: "integral quádrupla operador"] + - "⨍": [T: "integral de parte finita"] + - "⨎": [T: "integral com traço duplo"] + - "⨏": [T: "integral média com barra"] + - "⨐": [T: "circulação função"] + - "⨑": [T: "no sentido anti-horário integração"] + - "⨒": [T: "linha integração com retangular caminho ao redor polo"] + - "⨓": [T: "linha integração com semicircular caminho ao redor polo"] + - "⨔": [T: "linha integração não incluindo o polo"] + - "⨕": [T: "integral ao redor a ponto operador"] + - "⨖": [T: "quatérnio integral operador"] + - "⨗": [T: "seta integral com para a esquerda com gancho"] + - "⨘": [T: "integral com vezes sinal"] + - "⨙": [T: "integral com interseção"] + - "⨚": [T: "integral com união"] + - "⨛": [T: "integral com barra superior"] + - "⨜": [T: "integral com barra inferior"] + - "⨝": [T: "unir"] + - "⨞": [T: "triângulo grande esquerda operador"] + - "⨟": [T: "da notação z esquema composição"] + - "⨠": [T: "da notação z esquema encanamento"] + - "⨡": [T: "da notação z esquema projeção"] + - "⨢": [T: "círculo mais sinal com acima de"] + - "⨣": [T: "sinal de mais com circunflexo acento acima de"] + - "⨤": [T: "sinal de mais com til acima de"] + - "⨥": [T: "sinal de mais com ponto abaixo de"] + - "⨦": [T: "sinal de mais com til abaixo de"] + - "⨧": [T: "sinal de mais com subscrito dois"] + - "⨨": [T: "triângulo mais sinal com preto"] + - "⨩": [T: "sinal de menos com vírgula acima de"] + - "⨪": [T: "sinal de menos com ponto abaixo de"] + - "⨫": [T: "sinal de menos com descendo pontos"] + - "⨬": [T: "sinal de menos com subindo pontos"] + - "⨭": [T: "círculo mais sinal em esquerda metade"] + - "⨮": [T: "círculo mais sinal em direita metade"] + - "⨯": + - test: + if: "$Verbosity='Terse'" + then: [T: "cruzes"] + else: [T: "cruzes produto"] + - "⨰": [T: "sinal de multiplicação com ponto acima de"] + - "⨱": [T: "sinal de multiplicação com barra inferior"] + - "⨲": [T: "produto semidireto com inferior fechado"] + - "⨳": [T: "produto esmagado"] + - "⨴": [T: "círculo multiplicação sinal em esquerda metade"] + - "⨵": [T: "círculo multiplicação sinal em direita metade"] + - "⨶": [T: "sinal de multiplicação com circunflexo acento circulado"] + - "⨷": [T: "círculo multiplicação sinal em duplo"] + - "⨸": [T: "sinal de divisão circulado"] + - "⨹": [T: "triângulo mais sinal em"] + - "⨺": [T: "triângulo menos sinal em"] + - "⨻": [T: "triângulo multiplicação sinal em"] + - "⨼": [T: "interior produto"] + - "⨽": [T: "à direita interior produto"] + - "⨾": [T: "da notação z relacional composição"] + - "⨿": [T: "amálgama ou coproduto"] + - "⩀": [T: "interseção com ponto"] + - "⩁": [T: "união com sinal de menos"] + - "⩂": [T: "união com barra superior"] + - "⩃": [T: "interseção com barra superior"] + - "⩄": [T: "interseção com e lógico"] + - "⩅": [T: "união com ou lógico"] + - "⩆": [T: "união acima de interseção"] + - "⩇": [T: "interseção acima de união"] + - "⩈": [T: "união acima de barra acima de interseção"] + - "⩉": [T: "interseção acima de barra acima de união"] + - "⩊": [T: "união ao lado de e unido com união"] + - "⩋": [T: "interseção ao lado de e unido com interseção"] + - "⩌": [T: "união fechada com serifas"] + - "⩍": [T: "interseção fechada com serifas"] + - "⩎": [T: "quadrado duplo interseção"] + - "⩏": [T: "quadrado duplo união"] + - "⩐": [T: "união fechada com serifas e produto esmagado"] + - "⩑": [T: "e lógico com ponto acima de"] + - "⩒": [T: "ou lógico com ponto acima de"] + - "⩓": [T: "duplo e lógico"] + - "⩔": [T: "duplo ou lógico"] + - "⩕": [T: "dois cruzando e lógico"] + - "⩖": [T: "dois cruzando ou lógico"] + - "⩗": [T: "inclinado grande ou"] + - "⩘": [T: "inclinado grande e"] + - "⩙": [T: "ou lógico sobreposto e lógico"] + - "⩚": [T: "e lógico com do meio haste"] + - "⩛": [T: "ou lógico com do meio haste"] + - "⩜": [T: "e lógico com horizontal travessão"] + - "⩝": [T: "ou lógico com horizontal travessão"] + - "⩞": [T: "e lógico com barra dupla superior"] + - "⩟": [T: "e lógico com barra inferior"] + - "⩠": [T: "e lógico com barra dupla inferior"] + - "⩡": [T: "pequeno vê com barra inferior"] + - "⩢": [T: "ou lógico com barra dupla superior"] + - "⩣": [T: "ou lógico com barra dupla inferior"] + - "⩤": [T: "da notação z domínio antirrestrição"] + - "⩥": [T: "da notação z imagem antirrestrição"] + - "⩦": [T: "sinal de igual com ponto abaixo de"] + - "⩧": [T: "idêntico com ponto acima de"] + - "⩨": [T: "barra tripla horizontal com traço duplo vertical"] + - "⩩": [T: "barra tripla horizontal com traço triplo vertical"] + - "⩪": [T: "operador til com ponto acima de"] + - "⩫": [T: "operador til com subindo pontos"] + - "⩬": [T: "semelhante menos semelhante"] + - "⩭": [T: "congruente com ponto acima de"] + - "⩮": [T: "igual com asterisco"] + - "⩯": [T: "quase igual a com circunflexo acento"] + - "⩰": [T: "aproximadamente igual a ou igual a"] + - "⩱": [T: "sinal de igual acima de sinal de mais"] + - "⩲": [T: "sinal de mais acima de sinal de igual"] + - "⩳": [T: "sinal de igual acima de operador til"] + - "⩴": [T: "duplo dois pontos igual"] + - "⩵": [T: "dois consecutivo igual sinais"] + - "⩶": [T: "três consecutivo igual sinais"] + - "⩷": [T: "sinal de igual com dois pontos acima de e dois pontos abaixo de"] + - "⩸": [T: "equivalente com quatro pontos acima de"] + - "⩹": [T: "círculo menor que com dentro de"] + - "⩺": [T: "círculo maior que com dentro de"] + - "⩻": [T: "menor que com ponto de interrogação acima de"] + - "⩼": [T: "maior que com ponto de interrogação acima de"] + - "⩽": [T: "menor que ou inclinado igual a"] + - "⩾": [T: "maior que ou inclinado igual a"] + - "⩿": [T: "menor que ou inclinado igual a com ponto dentro de"] + - "⪀": [T: "maior que ou inclinado igual a com ponto dentro de"] + - "⪁": [T: "menor que ou inclinado igual a com ponto acima de"] + - "⪂": [T: "maior que ou inclinado igual a com ponto acima de"] + - "⪃": [T: "menor que ou inclinado igual a com ponto acima de direita"] + - "⪄": [T: "maior que ou inclinado igual a com ponto acima de esquerda"] + - "⪅": [T: "menor que ou aproximado"] + - "⪆": [T: "maior que ou aproximado"] + - "⪇": [T: "menor que e linha simples não igual a"] + - "⪈": [T: "maior que e linha simples não igual a"] + - "⪉": [T: "menor que e não aproximado"] + - "⪊": [T: "maior que e não aproximado"] + - "⪋": [T: "menor que acima de linha dupla igual acima de maior que"] + - "⪌": [T: "maior que acima de linha dupla igual acima de menor que"] + - "⪍": [T: "menor que acima de semelhante ou igual"] + - "⪎": [T: "maior que acima de semelhante ou igual"] + - "⪏": [T: "menor que acima de semelhante acima de maior que"] + - "⪐": [T: "maior que acima de semelhante acima de menor que"] + - "⪑": [T: "menor que acima de maior que acima de linha dupla igual"] + - "⪒": [T: "maior que acima de menor que acima de linha dupla igual"] + - "⪓": [T: "menor que acima de inclinado igual acima de maior que acima de inclinado igual"] + - "⪔": [T: "maior que acima de inclinado igual acima de menor que acima de inclinado igual"] + - "⪕": [T: "inclinado igual a ou menor que"] + - "⪖": [T: "inclinado igual a ou maior que"] + - "⪗": [T: "inclinado igual a ou menor que com ponto dentro de"] + - "⪘": [T: "inclinado igual a ou maior que com ponto dentro de"] + - "⪙": [T: "linha dupla igual a ou menor que"] + - "⪚": [T: "linha dupla igual a ou maior que"] + - "⪛": [T: "linha dupla inclinado igual a ou menor que"] + - "⪜": [T: "linha dupla inclinado igual a ou maior que"] + - "⪝": [T: "semelhante ou menor que"] + - "⪞": [T: "semelhante ou maior que"] + - "⪟": [T: "semelhante acima de menor que acima de sinal de igual"] + - "⪠": [T: "semelhante acima de maior que acima de sinal de igual"] + - "⪡": [T: "duplo aninhado menor que"] + - "⪢": [T: "duplo aninhado maior que"] + - "⪣": [T: "duplo aninhado menor que com barra inferior"] + - "⪤": [T: "maior que sobreposto menor que"] + - "⪥": [T: "maior que ao lado de menor que"] + - "⪦": [T: "menor que fechado por curva"] + - "⪧": [T: "maior que fechado por curva"] + - "⪨": [T: "menor que fechado por curva acima de inclinado igual"] + - "⪩": [T: "maior que fechado por curva acima de inclinado igual"] + - "⪪": [T: "menor que"] + - "⪫": [T: "maior que"] + - "⪬": [T: "menor que ou igual a"] + - "⪭": [T: "maior que ou igual a"] + - "⪮": [T: "sinal de igual com irregular acima de"] + - "⪯": [T: "precede acima de linha simples sinal de igual"] + - "⪰": [T: "sucede acima de linha simples sinal de igual"] + - "⪱": [T: "precede acima de linha simples não igual a"] + - "⪲": [T: "sucede acima de linha simples não igual a"] + - "⪳": [T: "precede acima de sinal de igual"] + - "⪴": [T: "sucede acima de sinal de igual"] + - "⪵": [T: "precede acima de não igual a"] + - "⪶": [T: "sucede acima de não igual a"] + - "⪷": [T: "precede acima de quase igual a"] + - "⪸": [T: "sucede acima de quase igual a"] + - "⪹": [T: "precede acima de não quase igual a"] + - "⪺": [T: "sucede acima de não quase igual a"] + - "⪻": [T: "duplo precede"] + - "⪼": [T: "duplo sucede"] + - "⪽": [T: "contido com ponto"] + - "⪾": [T: "contendo com ponto"] + - "⪿": [T: "contido com sinal de mais abaixo de"] + - "⫀": [T: "contendo com sinal de mais abaixo de"] + - "⫁": [T: "contido com sinal de multiplicação abaixo de"] + - "⫂": [T: "contendo com sinal de multiplicação abaixo de"] + - "⫃": [T: "contido em ou igual a com ponto acima de"] + - "⫄": [T: "contém ou igual a com ponto acima de"] + - "⫅": [T: "contido em acima de sinal de igual"] + - "⫆": [T: "contém acima de sinal de igual"] + - "⫇": [T: "contido em acima de operador til"] + - "⫈": [T: "contém acima de operador til"] + - "⫉": [T: "contido em acima de quase igual a"] + - "⫊": [T: "contém acima de quase igual a"] + - "⫋": [T: "contido acima de não igual a"] + - "⫌": [T: "contém acima de não igual a"] + - "⫍": [T: "quadrado esquerda aberto caixa operador"] + - "⫎": [T: "quadrado direita aberto caixa operador"] + - "⫏": [T: "fechado contido"] + - "⫐": [T: "fechado contendo"] + - "⫑": [T: "fechado contido ou igual a"] + - "⫒": [T: "fechado contendo ou igual a"] + - "⫓": [T: "contido acima de contendo"] + - "⫔": [T: "contendo acima de contido"] + - "⫕": [T: "contido acima de contido"] + - "⫖": [T: "contendo acima de contendo"] + - "⫗": [T: "contendo ao lado de contido"] + - "⫘": [T: "contendo ao lado de e unido por travessão com contido"] + - "⫙": [T: "pertence a abrindo baixo"] + - "⫚": [T: "forcado com t superior"] + - "⫛": [T: "interseção transversal"] + - "⫝̸": [T: "bifurcando"] + - "⫝": [T: "não bifurcante"] + - "⫞": [T: "curto taco à esquerda"] + - "⫟": [T: "curto taco para baixo"] + - "⫠": [T: "curto taco para cima"] + - "⫡": [T: "perpendicular com esse"] + - "⫢": [T: "barra vertical triplo catraca à direita"] + - "⫣": [T: "barra dupla vertical catraca à esquerda"] + - "⫤": [T: "barra vertical duplo catraca à esquerda"] + - "⫥": [T: "barra dupla vertical duplo catraca à esquerda"] + - "⫦": [T: "longo travessão a partir de esquerda membro de duplo vertical"] + - "⫧": [T: "curto taco com barra superior para baixo"] + - "⫨": [T: "curto taco com barra inferior para cima"] + - "⫩": [T: "curto taco acima de curto taco para baixo para cima"] + - "⫪": [T: "duplo taco para baixo"] + - "⫫": [T: "duplo taco para cima"] + - "⫬": [T: "traço duplo não sinal"] + - "⫭": [T: "traço invertido duplo não sinal"] + - "⫮": [T: "não divide com invertido negação barra"] + - "⫯": [T: "círculo vertical linha com acima de"] + - "⫰": [T: "círculo vertical linha com abaixo de"] + - "⫱": [T: "círculo baixo taco com abaixo de"] + - "⫲": [T: "paralelo com traço horizontal"] + - "⫳": [T: "paralelo com operador til"] + - "⫴": [T: "barra tripla vertical binário relação"] + - "⫵": [T: "barra tripla vertical com traço horizontal"] + - "⫶": [T: "triplo dois pontos operador"] + - "⫷": [T: "triplo aninhado menor que"] + - "⫸": [T: "triplo aninhado maior que"] + - "⫹": [T: "linha dupla inclinado menor ou igual a"] + - "⫺": [T: "linha dupla inclinado maior ou igual a"] + - "⫻": [T: "barra tripla binário relação"] + - "⫼": [T: "barra grande tripla operador vertical"] + - "⫽": [T: "barra dupla operador"] + - "⫾": [T: "barra branca vertical"] + - "⫿": [T: "barra branca vertical"] + - "⬀": [T: "seta branca para nordeste"] + - "⬁": [T: "seta branca para noroeste"] + - "⬂": [T: "seta branca para sudeste"] + - "⬃": [T: "seta branca para sudoeste"] + - "⬄": [T: "seta branca para a esquerda e para a direita"] + - "⬅": [T: "seta preta para a esquerda"] + - "⬆": [T: "seta preta para cima"] + - "⬇": [T: "seta preta para baixo"] + - "⬈": [T: "seta preta para nordeste"] + - "⬉": [T: "seta preta para noroeste"] + - "⬊": [T: "seta preta para sudeste"] + - "⬋": [T: "seta preta para sudoeste"] + - "⬌": [T: "seta preta para a esquerda e para a direita"] + - "⬍": [T: "seta preta para cima e para baixo"] + - "⬎": [T: "seta para a direita com ponta baixo"] + - "⬏": [T: "seta para a direita com ponta cima"] + - "⬐": [T: "seta para a esquerda com ponta baixo"] + - "⬑": [T: "seta para a esquerda com ponta cima"] + - "⬒": [T: "quadrado com superior metade preto"] + - "⬓": [T: "quadrado com inferior metade preto"] + - "⬔": [T: "quadrado com superior direita diagonal metade preto"] + - "⬕": [T: "quadrado com inferior esquerda diagonal metade preto"] + - "⬖": [T: "losango com esquerda metade preto"] + - "⬗": [T: "losango com direita metade preto"] + - "⬘": [T: "losango com superior metade preto"] + - "⬙": [T: "losango com inferior metade preto"] + - "⬚": [T: "caixa"] + - "⬛": [T: "quadrado preto grande"] + - "⬜": [T: "quadrado branco grande"] + - "⬝": [T: "quadrado preto muito pequeno"] + - "⬞": [T: "quadrado branco muito pequeno"] + - "⬟": [T: "pentágono preto"] + - "⬠": [T: "pentágono branco"] + - "⬡": [T: "hexágono branco"] + - "⬢": [T: "hexágono preto"] + - "⬣": [T: "hexágono horizontal preto"] + - "⬤": [T: "círculo preto grande"] + - "⬥": [T: "losango preto médio"] + - "⬦": [T: "losango branco médio"] + - "⬧": [T: "losango preto médio"] + - "⬨": [T: "losango branco médio"] + - "⬩": [T: "losango preto pequeno"] + - "⬪": [T: "losango preto pequeno"] + - "⬫": [T: "losango branco pequeno"] + - "⬬": [T: "elipse preta horizontal"] + - "⬭": [T: "elipse branca horizontal"] + - "⬮": [T: "elipse preta vertical"] + - "⬯": [T: "elipse branca vertical"] + - "⬰": [T: "seta para a esquerda com pequeno círculo"] + - "⬱": [T: "setas três para a esquerda"] + - "⬲": [T: "seta para a esquerda com circulado mais"] + - "⬳": [T: "seta longa serpenteante para a esquerda"] + - "⬴": [T: "seta de duas pontas para a esquerda com vertical traço"] + - "⬵": [T: "seta de duas pontas para a esquerda com duplo vertical traço"] + - "⬶": [T: "seta de duas pontas para a esquerda a partir de barra"] + - "⬷": [T: "seta de duas pontas de três traços para a esquerda"] + - "⬸": [T: "seta para a esquerda com pontilhado haste"] + - "⬹": [T: "seta para a esquerda com cauda com vertical traço"] + - "⬺": [T: "seta para a esquerda com cauda com duplo vertical traço"] + - "⬻": [T: "seta de duas pontas para a esquerda com cauda"] + - "⬼": [T: "seta de duas pontas para a esquerda com cauda com vertical traço"] + - "⬽": [T: "seta de duas pontas para a esquerda com cauda com duplo vertical traço"] + - "⬾": [T: "seta para a esquerda atravessado por xis"] + - "⬿": [T: "seta ondulada apontando diretamente esquerda"] + - "⭀": [T: "seta igual sinal acima de para a esquerda"] + - "⭁": [T: "seta invertida til operador acima de para a esquerda"] + - "⭂": [T: "seta para a esquerda acima de invertido quase igual a"] + - "⭃": [T: "seta para a direita atravessado por maior que"] + - "⭄": [T: "seta para a direita atravessado por contendo"] + - "⭅": [T: "seta quádrupla para a esquerda"] + - "⭆": [T: "seta quádrupla para a direita"] + - "⭇": [T: "seta invertida til operador acima de para a direita"] + - "⭈": [T: "seta para a direita acima de invertido quase igual a"] + - "⭉": [T: "seta til operador acima de para a esquerda"] + - "⭊": [T: "seta para a esquerda acima de quase igual a"] + - "⭋": [T: "seta para a esquerda acima de invertido operador til"] + - "⭌": [T: "seta para a direita acima de invertido operador til"] + - "⭐": [T: "estrela branca média"] + - "⭑": [T: "estrela preta pequena"] + - "⭒": [T: "estrela branca pequena"] + - "⭓": [T: "pentágono preto direita apontando"] + - "⭔": [T: "pentágono branco direita apontando"] + - "⭕": [T: "círculo grosso grande"] + - "⭖": [T: "grosso oval com oval dentro de"] + - "⭗": [T: "círculo grosso com círculo dentro de"] + - "⭘": [T: "círculo grosso"] + - "⭙": [T: "grosso circulado aspa"] + - "⸀": [T: "ângulo substituição marcador à direita"] + - "⸁": [T: "ângulo pontilhado substituição marcador à direita"] + - "⸂": [T: "abre colchete substituição"] + - "⸃": [T: "fecha colchete substituição"] + - "⸄": [T: "abre colchete pontilhado substituição"] + - "⸅": [T: "fecha colchete pontilhado substituição"] + - "⸆": [T: "elevado interpolação marcador"] + - "⸇": [T: "elevado pontilhado interpolação marcador"] + - "⸈": [T: "pontilhado transposição marcador marcador"] + - "⸉": [T: "abre colchete transposição"] + - "⸊": [T: "fecha colchete transposição"] + - "⸋": [T: "quadrado elevado"] + - "⸌": [T: "abre colchete elevado omissão"] + - "⸍": [T: "fecha colchete elevado omissão"] + - "⸎": [T: "editorial corônis"] + - "⸏": [T: "parágrafo"] + - "⸐": [T: "bifurcado parágrafo"] + - "⸑": [T: "invertido bifurcado parágrafo"] + - "⸒": [T: "hipodiástole"] + - "⸓": [T: "pontilhado óbelo"] + - "⸔": [T: "âncora para baixo"] + - "⸕": [T: "âncora para cima"] + - "⸖": [T: "pontilhado direita apontando ângulo"] + - "⸗": [T: "duplo oblíquo hífen"] + - "⸘": [T: "invertido interrobang"] + - "⸙": [T: "ramo de palmeira"] + - "⸚": [T: "hífen com trema"] + - "⸛": [T: "til com anel acima de"] + - "⸜": [T: "abre colchete baixo paráfrase"] + - "⸝": [T: "fecha colchete baixo paráfrase"] + - "⸞": [T: "til com ponto acima de"] + - "⸟": [T: "til com ponto abaixo de"] + - "⸠": [T: "barra vertical com pena à esquerda"] + - "⸡": [T: "barra vertical com pena à direita"] + - "⸢": [T: "superior esquerda metade colchete"] + - "⸣": [T: "superior direita metade colchete"] + - "⸤": [T: "inferior esquerda metade colchete"] + - "⸥": [T: "inferior direita metade colchete"] + - "⸦": [T: "abre colchete de lado u"] + - "⸧": [T: "fecha colchete de lado u"] + - "⸨": [T: "esquerda parênteses duplos"] + - "⸩": [T: "direita parênteses duplos"] + - "⸪": [T: "dois pontos sobre um ponto pontuação"] + - "⸫": [T: "um ponto sobre dois pontos pontuação"] + - "⸬": [T: "quatro ponto pontuação enquadrado"] + - "⸭": [T: "cinco ponto marca"] + - "⸮": [T: "ponto invertido de interrogação"] + - "ⸯ": [T: "til vertical"] + - "⸰": [T: "anel ponto"] + - "⸱": [T: "separador de palavras do meio ponto"] + - "⸲": [T: "invertido vírgula"] + - "⸳": [T: "elevado ponto"] + - "⸴": [T: "elevado vírgula"] + - "⸵": [T: "ponto invertido e vírgula"] + - "⸶": [T: "adaga com esquerda guarda"] + - "⸷": [T: "adaga com direita guarda"] + - "⸸": [T: "invertido adaga"] + - "⸹": [T: "superior metade seção sinal"] + - "⸺": [T: "dois em travessão"] + - "⸻": [T: "três em travessão"] + - "〃": [T: "idem marca"] + - "〈": [T: "abre colchete angular"] + - "〉": [T: "fecha colchete angular"] + - "《": [T: "abre colchete angular duplo"] + - "》": [T: "fecha colchete angular duplo"] + - "「": [T: "abre colchete canto"] + - "」": [T: "fecha colchete canto"] + - "『": [T: "abre colchete branco canto"] + - "』": [T: "fecha colchete branco canto"] + - "【": [T: "abre colchete preto lenticular"] + - "】": [T: "fecha colchete preto lenticular"] + - "〔": [T: "abre colchete concha de tartaruga"] + - "〕": [T: "fecha colchete concha de tartaruga"] + - "〖": [T: "abre colchete branco lenticular"] + - "〗": [T: "fecha colchete branco lenticular"] + - "〘": [T: "abre colchete branco concha de tartaruga"] + - "〙": [T: "fecha colchete branco concha de tartaruga"] + - "〚": [T: "abre colchete branco quadrado"] + - "〛": [T: "fecha colchete branco quadrado"] + - "〜": [T: "ondulado travessão"] + - "〰": [T: "ondulado travessão"] + - "㉈": [T: "dez sobre preto quadrado circulado"] + - "㉉": [T: "vinte sobre preto quadrado circulado"] + - "㉊": [T: "trinta sobre preto quadrado circulado"] + - "㉋": [T: "quarenta sobre pretos quadrado circulado"] + - "㉌": [T: "cinquenta sobre preto quadrado circulado"] + - "㉍": [T: "sessenta sobre preto quadrado circulado"] + - "㉎": [T: "setenta sobre preto quadrado circulado"] + - "㉏": [T: "oitenta sobre preto quadrado circulado"] + - "㉑": [T: "vinte um circulado"] + - "㉒": [T: "vinte dois circulado"] + - "㉓": [T: "vinte três circulado"] + - "㉔": [T: "vinte quatro circulado"] + - "㉕": [T: "vinte cinco circulado"] + - "㉖": [T: "vinte seis circulado"] + - "㉗": [T: "vinte sete circulado"] + - "㉘": [T: "vinte oito circulado"] + - "㉙": [T: "vinte nove circulado"] + - "㉚": [T: "trinta circulado"] + - "㉛": [T: "trinta um circulado"] + - "㉜": [T: "trinta dois circulado"] + - "㉝": [T: "trinta três circulado"] + - "㉞": [T: "trinta quatro circulado"] + - "㉟": [T: "trinta cinco circulado"] + - "㊱": [T: "trinta seis circulado"] + - "㊲": [T: "trinta sete circulado"] + - "㊳": [T: "trinta oito circulado"] + - "㊴": [T: "trinta nove circulado"] + - "㊵": [T: "quarenta circulado"] + - "㊶": [T: "quarenta um circulado"] + - "㊷": [T: "quarenta dois circulado"] + - "㊸": [T: "quarenta três circulado"] + - "㊹": [T: "quarenta quatro circulado"] + - "㊺": [T: "quarenta cinco circulado"] + - "㊻": [T: "quarenta seis circulado"] + - "㊼": [T: "quarenta sete circulado"] + - "㊽": [T: "quarenta oito circulado"] + - "㊾": [T: "quarenta nove circulado"] + - "㊿": [T: "cinquenta circulado"] + - "㋌": [T: "mercúrio"] + - "㋍": [T: "ergs"] + - "㋎": [T: "elétron volts"] + - "㋏": [T: "limitado responsabilidade sinal"] + - "㍱": [T: "hectopascals"] + - "㍲": [T: "daltons"] + - "㍳": [T: "unidades astronômicas"] + - "㍴": [T: "barras"] + - "㍵": [T: "o vê"] + - "㍶": [T: "parsecs"] + - "㍷": [T: "decimetros"] + - "㍸": [T: "decímetros ao quadrado"] + - "㍹": [T: "decímetros ao cubo"] + - "㍺": [T: "instrumental unidades"] + - "㎀": [T: "picoampères"] + - "㎁": [T: "nanoampères"] + - "㎂": [T: "microampères"] + - "㎃": [T: "miliampères"] + - "㎄": [T: "quiloampères"] + - "㎅": [T: "quilobytes"] + - "㎆": [T: "megabytes"] + - "㎇": [T: "gigabytes"] + - "㎈": [T: "calorias"] + - "㎉": [T: "quilocalorias"] + - "㎊": [T: "picofarads"] + - "㎋": [T: "nanofarads"] + - "㎌": [T: "microfarads"] + - "㎍": [T: "microgramas"] + - "㎎": [T: "miligramas"] + - "㎏": [T: "quilogramas"] + - "㎐": [T: "hertz"] + - "㎑": [T: "quilohertz"] + - "㎒": [T: "megahertz"] + - "㎓": [T: "gigahertz"] + - "㎔": [T: "terahertz"] + - "㎕": [T: "microlitros"] + - "㎖": [T: "mililitros"] + - "㎗": [T: "decilitros"] + - "㎘": [T: "quilolitros"] + - "㎙": [T: "femtometros"] + - "㎚": [T: "nanometros"] + - "㎛": [T: "micrometros"] + - "㎜": [T: "milimetros"] + - "㎝": [T: "centimetros"] + - "㎞": [T: "quilometros"] + - "㎟": [T: "milímetros ao quadrado"] + - "㎠": [T: "centímetros ao quadrado"] + - "㎡": [T: "metros ao quadrado"] + - "㎢": [T: "quilômetros ao quadrado"] + - "㎣": [T: "milímetros ao cubo"] + - "㎤": [T: "centímetros ao cubo"] + - "㎥": [T: "metros ao cubo"] + - "㎦": [T: "quilômetros ao cubo"] + - "㎧": [T: "metros por segundo"] + - "㎨": [T: "metros por segundo ao quadrado"] + - "㎩": [T: "pascals"] + - "㎪": [T: "quilopascals"] + - "㎫": [T: "megapascals"] + - "㎬": [T: "gigapascals"] + - "㎭": [T: "radianos"] + - "㎮": [T: "radianos por segundo"] + - "㎯": [T: "radianos por segundo ao quadrado"] + - "㎰": [T: "picosegundos"] + - "㎱": [T: "nanosegundos"] + - "㎲": [T: "microsegundos"] + - "㎳": [T: "milisegundos"] + - "㎴": [T: "picovolts"] + - "㎵": [T: "nanovolts"] + - "㎶": [T: "microvolts"] + - "㎷": [T: "milivolts"] + - "㎸": [T: "quilovolts"] + - "㎹": [T: "megavolts"] + - "㎺": [T: "picowatts"] + - "㎻": [T: "nanowatts"] + - "㎼": [T: "microwatts"] + - "㎽": [T: "miliwatts"] + - "㎾": [T: "quilowatts"] + - "㎿": [T: "megawatts"] + - "㏀": [T: "quiloohms"] + - "㏁": [T: "megaohms"] + - "㏂": [T: "atometros"] + - "㏃": [T: "becquereis"] + - "㏄": [T: "cúbico centímetros"] + - "㏅": [T: "candelas"] + - "㏆": [T: "coulombs por quilograma"] + - "㏇": [T: "maiúscula C, o, ponto final"] + - "㏈": [T: "decibéis"] + - "㏉": [T: "grays"] + - "㏊": [T: "hectares"] + - "㏋": [T: "cavalo-vapor"] + - "㏌": [T: "polegadas"] + - "㏍": [T: "quilokelvins"] + - "㏎": [T: "quilometros"] + - "㏏": [T: "nós"] + - "㏐": [T: "lúmens"] + - "㏑": [T: "logaritmo natural"] + - "㏒": [T: "logaritmo"] + - "㏓": [T: "lux"] + - "㏔": [T: "milibarns"] + - "㏕": [T: "mills"] + - "㏖": [T: "moles"] + - "㏗": [T: "pê agá"] + - "㏘": [T: "picometros"] + - "㏙": [T: "partes por milhão"] + - "㏚": [T: "petaroentgens"] + - "㏛": [T: "esferorradianos"] + - "㏜": [T: "sieverts"] + - "㏝": [T: "webers"] + - "㏞": [T: "volts por metro"] + - "㏟": [T: "ampères por metro"] + - "㏿": [T: "galões"] + - "": [T: "igual com chapéu abaixo de"] + - "": [T: "igual com mais acima de"] + - "": [T: "igual com mais abaixo de"] + - "": [T: "til com mais acima de"] + - "": [T: "til com mais abaixo de"] + - "": [T: "igual duplo sobre maior que"] + - "": [T: "igual duplo sobre menor que"] + - "": [T: "contém ou igual a"] + - "": [T: "contém ou igual a"] + - "": [T: "contido em ou igual a"] + - "": [T: "igual sobre menor que"] + - "": [T: "pertence a ou igual a"] + - "": [T: "igual a ou maior que"] + - "": [T: "aproximado contém"] + - "": [T: "aproximado contido em"] + - "": [T: "contém com ponto inclui como sub relação"] + - "": [T: "contido em com ponto é incluído em como sub relação"] + - "": [T: "igual com ponto abaixo de"] + - "": [T: "ponto sobre menos sobre ponto à direita à esquerda"] + - "": [T: "ponto sobre menos sobre ponto à esquerda à direita"] + - "": [T: "quase igual a menos"] + - "": [T: "quadrado duplo xícara"] + - "": [T: "quadrado duplo maiúscula"] + - "": [T: "menor que igual a ou maior que"] + - "": [T: "til com ponto"] + - "": [T: "til com dois pontos"] + - "": [T: "menor que maior ou igual a"] + - "": [T: "maior que menor ou igual a"] + - "": [T: "equivalente a ou menor que"] + - "": [T: "equivalente a ou maior que"] + - "": [T: "esquerda aberto caixa operador"] + - "": [T: "direita aberto caixa operador"] + - "": [T: "idêntico a com ponto"] + - "": [T: "maior que igual a ou menor que"] + - "": [T: "operador barra"] + - "": [T: "operador duplo barra"] + - "": [T: "operador triplo barra"] + - "": [T: "menor que ou aproximadamente igual a"] + - "": [T: "maior que ou aproximadamente igual a"] + - "": [T: "aninhado menor que"] + - "": [T: "aninhado maior que"] + - "": [T: "precede ou equivalente a"] + - "": [T: "sucede ou equivalente a"] + - "": [T: "precede sobre igual"] + - "": [T: "sucede sobre igual"] + - "": [T: "menor igual inclinado maior"] + - "": [T: "maior igual inclinado menor"] + - "": [T: "satisfeito por"] + - "": [T: "deitado esse"] + - "": [T: "não asserção"] + - "": [T: "duplo igual"] + - "": [T: "triplo igual"] + - "": [T: "régua atrasado"] + - "": [T: "alias delimitador"] + - "": [T: "subgrupo normal de com barra"] + - "": [T: "contém como subgrupo normal com barra"] + - "": [T: "implica arredondado"] + - "": [T: "sorriso barra inferior"] + - "": [T: "carranca barra superior"] + - "": [T: "contém ou quase igual a"] + - "": [T: "contido em ou quase igual a"] + - "": [T: "maior que quase igual a ou menor que"] + - "": [T: "menor que quase igual ou maior que"] + - "": [T: "duplo ou lógico"] + - "": [T: "duplo e lógico"] + - "": [T: "ou lógico com barra dupla abaixo de"] + - "": [T: "ou lógico com barra abaixo de"] + - "": [T: "quase igual sobre igual"] + - "": [T: "triângulo esquerda apontando com bissectando barra"] + - "": [T: "triângulo direita apontando com bissectando barra"] + - "": [T: "igual com linha pontilhada superior"] + - "": [T: "precede com dois pontos"] + - "": [T: "sucede com dois pontos"] + - "": [T: "menor que ou igual inclinado"] + - "": [T: "maior que ou igual inclinado"] + - "": [T: "aninhado muito muito menor que"] + - "": [T: "aninhado muito muito maior que"] + - "": [T: "diferença entre variante"] + - "": [T: "menor que maior que sobreposto"] + - "": [T: "ou lógico e lógico sobreposto"] + - "": [T: "contendo sobre contendo"] + - "": [T: "contido sobre contido"] + - "": [T: "contendo sobre contido"] + - "": [T: "contido sobre contendo"] + - "": [T: "barra tripla vertical"] + - "": [T: "pontos pareados quádruplos verticales"] + - "": [T: "perpendicular barra superior"] + - "": [T: "catraca barra dupla vertical à esquerda"] + - "": [T: "duplo catraca barra dupla vertical à esquerda"] + - "": [T: "perpendicular sobre invertido perpendicular"] + - "": [T: "duplo catraca barra vertical à esquerda"] + - "": [T: "ângulo esférico abrindo cima"] + - "": [T: "barra dupla"] + - "": [T: "ângulo com canto à direita"] + - "": [T: "barra vertical circulado"] + - "": [T: "sinal de divisão circulado"] + - "": [T: "barra tracejada"] + - "": [T: "barra tracejada invertida"] + - "": [T: "linha tracejada média"] + - "": [T: "barra tracejada vertical"] + - "": [T: "perpendicular com esse"] + - "": [T: "ângulo com esse"] + - "": [T: "ângulo esférico abrindo esquerda"] + - "": [T: "ângulo abrindo esquerda"] + - "": [T: "barra vertical com gancho duplo"] + - "": [T: "ponto médio operador radical livre"] + - "": [T: "triângulo branco cima apontando acima de barra"] + - "": [T: "idêntico e paralelo a"] + - "": [T: "produto esmagado"] + - "": [T: "operador triplo barra com barra horizontal"] + - "": [T: "idêntico a com barra dupla"] + - "": [T: "barras triplas cruzadas"] + - "": [T: "círculo vertical barra sobre"] + - "": [T: "vertical proporcional a"] + - "": [T: "preto último um quarto lua"] + - "": [T: "preto primeiro um quarto lua"] + - "": [T: "negativo seno ondulado"] + - "": [T: "ponto entre parênteses"] + - "": [T: "parênteses"] + - "": [T: "branco sorriso"] + - "": [T: "branco carranca"] + - "": [T: "hexágono"] + - "": [T: "equivalente a sobre mais"] + - "": [T: "mais sobre equivalente a"] + - "": [T: "interseção serifas"] + - "": [T: "união serifas"] + - "": [T: "quadrado interseção serifas"] + - "": [T: "quadrado união serifas"] + - "": [T: "precede equivalente a ou sucede"] + - "": [T: "sucede equivalente a ou precede"] + - "": [T: "precede quase igual a ou sucede"] + - "": [T: "sucede quase igual a ou precede"] + - "": [T: "menor que equivalente a ou maior que"] + - "": [T: "maior que equivalente a ou menor que"] + - "": [T: "não vertical muito menor que"] + - "": [T: "não vertical muito maior que"] + - "": [T: "não muito menor que variante"] + - "": [T: "não muito maior que variante"] + - "": [T: "menor vertical não duplo igual"] + - "": [T: "maior que vertical não duplo igual"] + - "": [T: "não menor ou igual a"] + - "": [T: "não maior ou igual a"] + - "": [T: "nem igual a nem menor que"] + - "": [T: "não contém ou igual a"] + - "": [T: "nem contém nem igual a"] + - "": [T: "nem contido em nem igual a"] + - "": [T: "barra invertida contido"] + - "": [T: "nem igual a nem maior que"] + - "": [T: "não menos operador til"] + - "": [T: "nem igual a nem menor que"] + - "": [T: "não operador til"] + - "": [T: "não pertence a ou igual a"] + - "": [T: "nem igual a nem maior que"] + - "": [T: "não quase igual"] + - "": [T: "não sucede semelhante"] + - "": [T: "menor que ou inclinado igual a com barra"] + - "": [T: "maior que ou inclinado igual a com barra"] + - "": [T: "contendo barra"] + - "": [T: "não contém"] + - "": [T: "não menor ou igual a"] + - "": [T: "não maior ou igual a"] + - "": [T: "não quase igual a menos"] + - "": [T: "negado conjunto pertinência ponto acima de"] + - "": [T: "não ângulo vertical"] + - "": [T: "não paralelo inclinado"] + - "": [T: "não operador barra"] + - "": [T: "não operador duplo barra"] + - "": [T: "não operador triplo barra"] + - "": [T: "menor que mas não aproximadamente igual a"] + - "": [T: "maior que mas não aproximadamente igual a"] + - "": [T: "menor que ou não igual a"] + - "": [T: "maior que ou não igual a"] + - "": [T: "não aninhado menor que"] + - "": [T: "não aninhado maior que"] + - "": [T: "não muito menor que"] + - "": [T: "não muito maior que"] + - "": [T: "precede mas não equivalente a"] + - "": [T: "sucede mas não equivalente a"] + - "": [T: "precede mas não igual a"] + - "": [T: "sucede mas não igual a"] + - "": [T: "não igual ou precede"] + - "": [T: "não igual ou sucede"] + - "": [T: "precede mas não igual a"] + - "": [T: "sucede mas não igual a"] + - "": [T: "não contido em nem igual a"] + - "": [T: "não contém nem igual a"] + - "": [T: "contido em ou não igual a"] + - "": [T: "contém ou não igual a"] + - "": [T: "não contido em nem igual a"] + - "": [T: "não contém nem igual a"] + - "": [T: "não triplo menor que"] + - "": [T: "não triplo maior que"] + - "": [T: "não precede igual"] + - "": [T: "não sucede igual"] + - "": [T: "não subgrupo normal de com barra"] + - "": [T: "não contém como subgrupo normal com barra"] + - "": [T: "não diferença entre"] + - "": [T: "não geometricamente equivalente a"] + - "": [T: "não vertical semelhante"] + - "": [T: "não igual ou semelhante"] + - "": [T: "não vertical aproximado"] + - "": [T: "não aproximadamente idêntico a"] + - "": [T: "não irregular igual"] + - "": [T: "não irregular simples igual"] + - "": [T: "não igual ponto"] + - "": [T: "invertido não equivalente"] + - "": [T: "quadrado não contido"] + - "": [T: "quadrado não contendo"] + - "": [T: "não quase igual sobre igual"] + - "": [T: "não estritamente equivalente a"] + - "": [T: "não congruente ponto"] + - "": [T: "invertido não igual"] + - "": [T: "triângulo não vertical esquerda igual"] + - "": [T: "triângulo não vertical direita igual"] + - "": [T: "não parcial"] + - "": [T: "seta extensor"] + - "": [T: "seta para a direita sobre seta esquerda"] + - "": [T: "seta para a direita sobre seta esquerda"] + - "": [T: "arpão para a direita sobre arpão esquerda"] + - "": [T: "arpão para a direita sobre arpão esquerda"] + - "": [T: "seta dupla para nordeste e para sudoeste"] + - "": [T: "seta dupla para noroeste e para sudeste"] + - "": [T: "arpão horizontal extensor"] + - "": [T: "seta no sentido anti-horário arco para a esquerda"] + - "": [T: "seta no sentido anti-horário arco para a direita"] + - "": [T: "seta grande para a direita acento"] + - "": [T: "seta grande para a esquerda acento"] + - "": [T: "ponta de seta à esquerda"] + - "": [T: "ponta de seta à direita"] + - "": [T: "seta grande para a esquerda e para a direita com traço"] + - "": [T: "seta horizontal dupla extensor"] + - "": [T: "seta grande dupla para a esquerda e para a direita com traço"] + - "": [T: "seta para baixo à esquerda de seta para cima"] + - "": [T: "seta para a esquerda com canto baixo"] + - "": [T: "seta para a direita com canto cima"] + - "": [T: "seta para a esquerda com canto cima"] + - "": [T: "seta semicircular superior no sentido anti-horário com mais"] + - "": [T: "seta semicircular superior no sentido horário com menos"] + - "": [T: "seta para a direita com cauda com traço"] + - "": [T: "arpão para a direita com farpa para baixo"] + - "": [T: "arpão para a esquerda com farpa para baixo"] + - "": [T: "arpão para a esquerda e para a direita baixo"] + - "": [T: "arpão para a esquerda e para a direita cima"] + - "": [T: "arpão para cima e para baixo esquerda"] + - "": [T: "arpão para cima e para baixo direita"] + - "": [T: "seta para cima à direita de baixo seta"] + - "": [T: "arpão para a esquerda a barra com farpa cima"] + - "": [T: "arpão para a direita a barra com farpa cima"] + - "": [T: "arpão para a esquerda a barra com farpa baixo"] + - "": [T: "arpão para a direita a barra com farpa baixo"] + - "": [T: "arpão para a esquerda a partir de barra com farpa cima"] + - "": [T: "arpão para a direita a partir de barra com farpa cima"] + - "": [T: "arpão para a esquerda a partir de barra com farpa baixo"] + - "": [T: "arpão para a direita a partir de barra com farpa baixo"] + - "": [T: "arpão para cima a barra com farpa esquerda"] + - "": [T: "arpão para baixo a barra com farpa esquerda"] + - "": [T: "arpão para cima a barra com farpa direita"] + - "": [T: "arpão para baixo a barra com farpa direita"] + - "": [T: "arpão para cima a partir de barra com farpa esquerda"] + - "": [T: "arpão para baixo a partir de barra com farpa esquerda"] + - "": [T: "arpão para cima a partir de barra com farpa direita"] + - "": [T: "arpão para baixo a partir de barra com farpa direita"] + - "": [T: "seta para cima a barra"] + - "": [T: "seta para baixo a barra"] + - "": [T: "arpão para cima à esquerda de baixo arpão"] + - "": [T: "arpão para cima à direita de baixo arpão"] + - "": [T: "ponta de seta para cima"] + - "": [T: "ponta de seta para baixo"] + - "": [T: "arpão duplo com esquerda farpa baixo direita farpa cima"] + - "": [T: "arpão duplo com esquerda farpa cima direita farpa baixo"] + - "": [T: "seta para a esquerda barra superior"] + - "": [T: "seta para a direita barra superior"] + - "": [T: "seta para a esquerda barra inferior"] + - "": [T: "seta para a direita barra inferior"] + - "": [T: "seta tripla para a esquerda e para a direita"] + - "": [T: "seta dupla para nordeste e para sudeste"] + - "": [T: "seta semicircular no sentido anti-horário para a esquerda"] + - "": [T: "seta semicircular no sentido horário para a esquerda"] + - "": [T: "seta esquerda aberta círculo para a esquerda e para a direita"] + - "": [T: "seta para a direita sobre til"] + - "": [T: "seta para a esquerda sobre til"] + - "": [T: "arpão para a esquerda barra superior"] + - "": [T: "arpão para a direita barra superior"] + - "": [T: "arpão para a esquerda barra inferior"] + - "": [T: "arpão para a direita barra inferior"] + - "": [T: "seta achatado preta para a esquerda"] + - "": [T: "seta semicircular no sentido horário para a direita"] + - "": [T: "seta semicircular no sentido anti-horário para a direita"] + - "": [T: "arpão esquerda aberto círculo para a esquerda e para a direita"] + - "": [T: "seta para cima esquerda de barra vertical"] + - "": [T: "seta para baixo esquerda de barra vertical"] + - "": [T: "seta para cima direita de barra vertical"] + - "": [T: "seta para baixo direita de barra vertical"] + - "": [T: "seta para a direita com estendido baixo gancho"] + - "": [T: "seta para a esquerda com estendido gancho"] + - "": [T: "seta para a esquerda com estendido baixo gancho"] + - "": [T: "seta para a direita com estendido gancho"] + - "": [T: "seta não para a direita ondulado"] + - "": [T: "seta não para a direita curvo"] + - "": [T: "arpão para cima esquerda de barra vertical"] + - "": [T: "arpão para baixo esquerda de barra vertical"] + - "": [T: "arpão para cima direita de barra vertical"] + - "": [T: "arpão para baixo direita de barra vertical"] + - "": [T: "seta vertical dupla extensor"] + - "": [T: "arpão vertical com farpa esquerda extensor"] + - "": [T: "arpão vertical com farpa direita extensor"] + - "": [T: "arpão para a direita sobre esquerda arpão direita"] + - "": [T: "arpão para a direita sobre esquerda arpão esquerda"] + - "": [T: "arpão para a esquerda sobre direita arpão direita"] + - "": [T: "arpão para a esquerda sobre direita arpão esquerda"] + - "": [T: "seta para a esquerda a partir de barra ponta de seta"] + - "": [T: "seta para a esquerda e para a direita a partir de barra extensor"] + - "": [T: "seta para a esquerda a partir de barra cauda"] + - "": [T: "seta para a direita a partir de barra cauda"] + - "": [T: "seta para a direita a partir de barra ponta de seta"] + - "": [T: "arpão para cima a partir de barra com farpa esquerda ponta de seta"] + - "": [T: "seta para a direita sobre esquerda seta direita"] + - "": [T: "seta para a direita sobre esquerda seta esquerda"] + - "": [T: "seta para a esquerda sobre direita seta direita"] + - "": [T: "seta para a esquerda sobre direita seta esquerda"] + - "": [T: "seta para cima a partir de barra ponta de seta"] + - "": [T: "seta para cima a partir de barra cauda"] + - "": [T: "seta para baixo a partir de barra cauda"] + - "": [T: "seta para baixo a partir de barra ponta de seta"] + - "": [T: "arpão para baixo a partir de barra com farpa direita ponta de seta"] + - "": [T: "arpão para cima à esquerda de baixo arpão inferior"] + - "": [T: "arpão para cima à esquerda de baixo arpão extensor"] + - "": [T: "arpão para baixo à esquerda de cima arpão superior"] + - "": [T: "arpão para cima à esquerda de baixo arpão superior"] + - "": [T: "arpão para baixo à esquerda de o cima arpão extensor"] + - "": [T: "arpão para baixo à esquerda de o cima arpão inferior"] + - "": [T: "seta para cima esquerda de baixo seta inferior"] + - "": [T: "seta para baixo esquerda de cima seta superior"] + - "": [T: "seta para cima esquerda de baixo seta superior"] + - "": [T: "seta para baixo esquerda de cima seta inferior"] + - "": [T: "setas para a esquerda e para a direita extensor"] + - "": [T: "seta para nordeste extensor"] + - "": [T: "seta para noroeste extensor"] + - "": [T: "baixo apontando chave esquerda"] + - "": [T: "baixo apontando chave médio"] + - "": [T: "baixo apontando chave direita"] + - "": [T: "chave horizontal extensor"] + - "": [T: "cima apontando chave esquerda"] + - "": [T: "cima apontando chave médio"] + - "": [T: "apontando para chave direita para cima"] + - "": [T: "barra vertical à esquerda"] + - "": [T: "barra vertical à direita"] + - "": [T: "esquerda barra dupla vertical"] + - "": [T: "direita barra dupla vertical"] + - "": [T: "colchete horizontal extensor"] + - "": [T: "quadrado sob colchete"] + - "⎵": [T: "quadrado sob colchete"] + - "": [T: "quadrado sobre colchete"] + - "⎴": [T: "quadrado sobre colchete"] + - "": [T: "sob colchete esquerda"] + - "": [T: "sob colchete direita"] + - "": [T: "sobre colchete esquerda"] + - "": [T: "sobre colchete direita"] + - "": [T: "abre parênteses 1"] + - "": [T: "abre parênteses 2"] + - "": [T: "abre parênteses 3"] + - "": [T: "abre parênteses 4"] + - "": [T: "fecha parênteses 1"] + - "": [T: "fecha parênteses 2"] + - "": [T: "fecha parênteses 3"] + - "": [T: "fecha parênteses 4"] + - "": [T: "radical 1"] + - "": [T: "radical 2"] + - "": [T: "radical 3"] + - "": [T: "radical 4"] + - "": [T: "radical 5"] + - "": [T: "radical inferior"] + - "": [T: "radical extensor vertical"] + - "": [T: "radical superior"] + - "": [T: "esquerda colchete branco superior"] + - "": [T: "esquerda colchete branco extensor"] + - "": [T: "esquerda colchete branco inferior"] + - "": [T: "direita colchete branco superior"] + - "": [T: "direita colchete branco extensor"] + - "": [T: "direita colchete branco inferior"] + - "": [T: "abre colchete branco encaracolado"] + - "": [T: "fecha colchete branco encaracolado"] + - "": [T: "sinal longo de divisão"] + - "": [T: "sinal longo de divisão extensor"] + - "": [T: "divisão curta"] + - "": [T: "duplo sudoeste a nordeste ligação em"] + - "": [T: "duplo noroeste a sudeste ligação em"] + - "": [T: "simples horizontal ligação em"] + - "": [T: "duplo horizontal ligação em"] + - "": [T: "triplo horizontal ligação em"] + - "": [T: "simples vertical ligação em"] + - "": [T: "duplo vertical ligação em"] + - "": [T: "triplo vertical ligação em"] + - "": [T: "menor que ligação em"] + - "": [T: "maior que ligação em"] + - "": [T: "simples horizontal ligação en"] + - "": [T: "duplo horizontal ligação en"] + - "": [T: "triplo horizontal ligação en"] + - "": [T: "retângulo superior esquerda"] + - "": [T: "retângulo inferior esquerda"] + - "": [T: "retângulo superior direita"] + - "": [T: "retângulo inferior direita"] + - "": [T: "divisão sintética canto"] + - "": [T: "divisão sintética extensor horizontal"] + - "": [T: "divisão sintética extensor vertical"] + - "": [T: "esquerda teto piso extensor"] + - "": [T: "direita teto piso extensor"] + - "": [T: "sobre colchete extensor"] + - "": [T: "barra extensor vertical"] + - "": [T: "esquerda barra dupla extensor vertical"] + - "": [T: "barra extensor horizontal"] + - "": [T: "sob colchete extensor"] + - "": [T: "baixo apontando parêntese direita"] + - "": [T: "baixo apontando parêntese extensor"] + - "": [T: "baixo apontando parêntese esquerda"] + - "": [T: "cima apontando chave extensor"] + - "": [T: "cima apontando parêntese esquerda"] + - "": [T: "cima apontando parêntese extensor"] + - "": [T: "cima apontando parêntese direita"] + - "": [T: "baixo apontando chave extensor"] + - "": [T: "planck constante sobre dois pi barra"] + - "": [T: "g espelhado"] + - "": [T: "sem ponto jota"] + - "": [T: "digama"] + - "ϝ": [T: "digama"] + - "": [T: "dê"] + - "": [T: "e"] + - "": [T: "i"] + - "": [T: "jota"] + - "ⅅ": + - spell: "translate('.', 'ⅅ', 'DD')" # 0xed16, 0x2145 + - "": [T: "no sentido anti-horário contorno integral laço"] + - "": [T: "no sentido horário contorno integral laço"] + - "": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: "junção status"] + - "": [T: "junção status esquerda"] + - "": [T: "junção status direita"] + - "": [T: "junção status extensor"] + - "": [T: "integral laço"] + - "": [T: "integral laço duplo"] + - "": [T: "integral laço triplo"] + - "": [T: "expandindo integral laço duplo"] + - "": [T: "expandindo integral laço triplo"] + - "": [T: "assintoticamente igual a acento"] + - "": [T: "sinal de igual acento"] + - "": [T: "linha quádrupla"] + - "": [T: "círculo barra acento com aberto esquerda"] + - "": [T: "círculo barra acento com fechado esquerda"] + - "": [T: "círculo barra acento com aberto direita"] + - "": [T: "barra acento com sobre ponto"] + - "": [T: "barra acento com sob ponto"] + - "": [T: "barra acento com duplo sobre ponto"] + - "": [T: "barra acento com duplo sob ponto"] + - "": [T: "barra acento com circunflexo"] + - "": [T: "barra espessa acento inferior"] + - "": [T: "círculo barra acento com fechado direita"] + - "": [T: "ponto grande acima de"] + - "": [T: "alinhamento marca"] + - "": [T: ""] + - "​": [T: ""] + - "": [T: ""] + - " ": [T: ""] + - "": [T: ""] + - " ": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: ""] + - " ": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: ""] + - "": [T: "ausente termo"] + - "": [T: "seta no sentido horário contorno integral sobre esquerda"] + - "": [T: "quadrado integral com"] + - "": [T: "integral com barra"] + - "": [T: "integral invertida"] + - "": [T: "duplo zero sobre duplo zero"] + - "": [T: "zero com barra"] + - "-": + - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "": + - 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: "maiúscula"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + replace: [T: "ligadura a e"] + - "": + - 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: "maiúscula"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + replace: [T: "sustenido esse"] + - "": + - 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: "maiúscula"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + replace: [T: "o com traço"] + - "": + - spell: "translate('.', '', 'ΔΨΛΠΣΘΓΩΥ')" + - T: "vazado" + - "-": + - spell: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + - T: "vazado" + - "": [T: "vazado final sigma"] + - "": [T: "vazado rô"] + - "": [T: "vazado fi"] + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - "𝐴-𝑍": + - spell: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "-": + - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝑎-𝑧": + - spell: "translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔𝑕𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz')" + - "-": + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - "𝖠-𝖹": + - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "-": + - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝖺-𝗓": + - spell: "translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz')" + - "-": + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - "𝗔-𝗭": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝗮-𝘇": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - "𝘈-𝘡": + - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "-": + - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝘢-𝘻": + - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" + - "-": + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - "𝘼-𝙕": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝙖-𝙯": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - "𝙰-𝚉": + - spell: "translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "-": + - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝚊-𝚣": + - spell: "translate('.', '𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣', 'abcdefghijklmnopqrstuvwxyz')" + - "-": + - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - "": [T: "sem ponto i"] + - "𝚤": [T: "sem ponto i"] + - "𝚥": [T: "sem ponto jota"] + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + - "": [T: "em negrito nábela"] + - "𝛁": [T: "em negrito nábela"] + - "𝛛𝛜𝛝𝛞𝛟𝛠𝛡": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '𝛛𝛜𝛝𝛞𝛟𝛠𝛡', '∂εθκφρπ')" + - "": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', '∂εθκφρπ')" + - "𝛢-𝛺": + - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹𝛺', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + - "-": + - spell: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + - "𝛼-𝜔": + - spell: "translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌𝜍𝜎𝜏𝜐𝜑𝜒𝜓𝜔', 'αβγδεζηθικλμνξοπρςστυφχψω')" + - "-": + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + - "": [T: "itálico nábela"] + - "𝛻": [T: "itálico nábela"] + - "𝜕𝜖𝜗𝜘𝜙𝜚𝜛": + - spell: "translate('.', '𝜕𝜖𝜗𝜘𝜙𝜚𝜛', '∂εθκφρπ')" + - "": + - spell: "translate('.', '', '∂εθκφρπ')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + - "𝝏𝝐𝝑𝝒𝝓𝝔𝝕": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '𝝏𝝐𝝑𝝒𝝓𝝔𝝕', '∂εθκφρπ')" + - "": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', '∂εθκφρπ')" + - "𝜵": [T: "em negrito itálico nábela"] + - "": [T: "em negrito itálico nábela"] + - "𝝖-𝝮": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭𝝮', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + - "𝝰-𝞈": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿𝞀𝞁𝞂𝞃𝞄𝞅𝞆𝞇𝞈', 'αβγδεζηθικλμνξοπρςστυφχψω')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + - "𝞉𝞊𝞋𝞌𝞍𝞎𝞏": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '𝞉𝞊𝞋𝞌𝞍𝞎𝞏', '∂εθκφρπ')" + - "": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', '∂εθκφρπ')" + - "": [T: "em negrito nábela"] + - "𝝯": [T: "em negrito nábela"] + - "𝞐-𝞨": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + - "𝞪-𝟂": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺𝞻𝞼𝞽𝞾𝞿𝟀𝟁𝟂', 'αβγδεζηθικλμνξοπρςστυφχψω')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + - "𝟃𝟄𝟅𝟆𝟇𝟈𝟉": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '𝟃𝟄𝟅𝟆𝟇𝟈𝟉', '∂εθκφρπ')" + - "": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', '∂εθκφρπ')" + - "": [T: "em negrito nábela"] + - "𝞩": [T: "em negrito nábela"] + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', '0123456789')" + - "𝟎-𝟗": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789')" + - "-": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '', '0123456789')" + - "𝟬-𝟵": + - test: + if: "not($IgnoreBold)" + then: [T: "negrito"] + - spell: "translate('.', '𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵', '0123456789')" + - "-": + - spell: "translate('.', '', '0123456789')" + - "𝟢-𝟫": + - spell: "translate('.', '𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫', '0123456789')" + - "𝟶-𝟿": + - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" + - "": [T: "desconhecido caractere"] + - "": [T: "triângulos inferior direita e inferior esquerda"] + - "": [T: "horizontal reticências extensor"] + - "": [T: "linha média horizontal reticências extensor"] + - "": [T: "radical extensor"] + - "": [T: "seta vertical extensor"] + - "": [T: "seta horizontal extensor"] + - "": [T: "registrado sinal sem serifa"] + - "": [T: "direitos autorais sinal sem serifa"] + - "": [T: "marca registrada sinal sem serifa"] + - "": [T: "parêntese superior à esquerda"] + - "": [T: "parêntese extensor à esquerda"] + - "": [T: "parêntese inferior à esquerda"] + - "": [T: "colchete superior à esquerda"] + - "": [T: "colchete extensor à esquerda"] + - "": [T: "colchete inferior à esquerda"] + - "": [T: "chave superior à esquerda"] + - "": [T: "chave médio à esquerda"] + - "": [T: "chave inferior à esquerda"] + - "": [T: "chave extensor"] + - "": [T: "integral extensor"] + - "": [T: "parêntese superior à direita"] + - "": [T: "parêntese extensor à direita"] + - "": [T: "parêntese inferior à direita"] + - "": [T: "colchete superior à direita"] + - "": [T: "colchete extensor à direita"] + - "": [T: "colchete inferior à direita"] + - "": [T: "chave superior à direita"] + - "": [T: "chave médio à direita"] + - "": [T: "chave inferior à direita"] + - "": [T: "logo da apple"] + - "ff": [T: "ligadura ff"] + - "fi": [T: "ligadura fi"] + - "fl": [T: "ligadura fl"] + - "ffi": [T: "ligadura ffi"] + - "ffl": [T: "ligadura ffl"] + - "ſt": [T: "pés"] + - "st": [T: "st"] + - "﬩": [T: "letra hebraica alternativa mais"] + - "︠": [T: "ligadura esquerda metade"] + - "︡": [T: "ligadura direita metade"] + - "︢": [T: "til duplo esquerda metade"] + - "︣": [T: "til duplo direita metade"] + - "︤": [T: "mácron esquerda metade"] + - "︥": [T: "mácron direita metade"] + - "︦": [T: "conjugando mácron"] + - "︵": [T: "sobre parêntese"] + - "︶": [T: "sob parêntese"] + - "︷": [T: "sobre chave"] + - "︸": [T: "sob chave"] + - "︿": [T: "sobre colchete angular"] + - "﹀": [T: "sob colchete angular"] + - "﹡": [T: "pequeno asterisco"] + - "﹢": [T: "pequeno mais"] + - "﹣": [T: "pequeno menos"] + - "﹤": [T: "pequeno menor que"] + - "﹥": [T: "pequeno maior que"] + - "﹦": [T: "pequeno igual"] + - "+": [T: "de largura total sinal de mais"] + - "<": [T: "menor que"] + - "=": [T: "igual"] + - ">": [T: "maior que"] + - "\": [T: "barra invertida"] + - "^": [T: "chapéu"] + - "|": + - 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: "dado"] + else: [T: "barra vertical"] + - else_if: "not(preceding-sibling::*) or not(following-sibling::*)" + then: [T: "barra vertical"] + - else_if: "$ClearSpeak_VerticalLine = 'SuchThat'" + then: [T: "tal que"] + - else_if: "$ClearSpeak_VerticalLine = 'Given' or $DefaultToGiven" + then: [T: "dado"] + - else: [T: "divide"] + - "~": [T: "til"] + - "¬": [T: "não"] + - "←": [T: "seta para a esquerda"] + - "↑": [T: "seta para cima"] + - "→": [T: "seta para a direita"] + - "↓": [T: "seta para baixo"] + - "": [T: "desconhecido ou ausente objeto"] + - "�": [T: "ausente caractere"] + - "🣑": [T: "é em equilíbrio com"] + - "🣒": [T: "está em equilíbrio deslocado para a direita com"] + - "🣓": [T: "está em equilíbrio deslocado para a esquerda com"] +# ==== FIM DO RECUO CONTROLADO ==== diff --git a/Rules/Languages/pt/unicode.yaml b/Rules/Languages/pt/unicode.yaml new file mode 100644 index 000000000..64b3fb518 --- /dev/null +++ b/Rules/Languages/pt/unicode.yaml @@ -0,0 +1,571 @@ +--- + # 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-z": + - test: + if: "$TTS='none'" + then: [T: "."] + else: [spell: "'.'"] + - "A-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: "maiúscula"] # (en: 'cap', google translation) + 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('.', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"] + + - "0-9": [T: "."] # (en: '.') + + - " ": [t: " "] # 0x20 + + - "!": # 0x21 + # Restaura a ramificação do inglês: dentro de :literal: é pontuação, fora + # é o operador. Sem isto, um ponto de exclamação literal é lido "fatorial". + - test: + if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" + then_test: + if: "$Verbosity = 'Terse'" + then: [T: "exclamação"] # (en: 'bang') + else: [T: "ponto de exclamação"] # (en: 'exclamation point') + else: [T: "fatorial"] # (en: 'factorial') + - "\"": [T: "aspas"] # 0x22 (en: 'quotation mark') + - "#": [T: "número"] # 0x23 (en: 'number', MathPlayer: 'signo de número', google: 'número') + - "$": [T: "dólares"] # 0x24 (en: 'dollars') + - "%": [T: "por cento"] # 0x25 (en: 'percent') + - "&": [T: "e comercial"] # 0x26 + - "'": [T: "apóstrofo"] # 0x27 (en: 'apostrophe', MathPlayer: 'apóstrofo', google: 'apóstrofe') + - "(": # 0x28 + - test: + if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' + then_test: + if: "$Verbosity='Terse'" + then: [T: "abre parênteses"] # (en: 'open', google translation) + else: [T: "abre parênteses"] # (en: 'open paren', MathPlayer: 'paréntesis', google: 'paréntesis abierto') + else: [T: "abre parênteses"] # (en: 'left paren', MathPlayer: 'paréntesis', google: 'paréntesis dejado') + - ")": # 0x29 + - test: + if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' + then_test: + if: "$Verbosity='Terse'" + then: [T: "fecha parênteses"] # (en: 'close', google translation) + else: [T: "fecha parênteses"] # (en: 'close paren', MathPlayer: 'cierra paréntesis', google: 'paréntesis cercana') + else: [T: "fecha parênteses"] # (en: 'right paren', MathPlayer: 'cierra paréntesis') + + - "*": # 0x2a + test: + if: "parent::*[name(.)='msup' or name(.)='msubsup']" + then: [T: "asterisco"] # (en: 'star', google translation) + else: [T: "vezes"] # (en: 'times') + - "+": [T: "mais"] # 0x2b (en: 'plus') + - ",": # 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: "vírgula" # (en: 'comma', google translation) + - test: + if: "$Verbosity != Terse" + then: [pause: short] + # else silent + + - "-": [T: "menos"] # 0x2d (en: 'minus') + - ".": # 0x2e + - test: + if: "parent::*[1][self::m:mn]" + then: [T: "ponto"] # (en: 'point', google translation) + else: [T: "ponto"] # (en: 'dot') + - "/": [T: "barra"] # 0x2f (en: 'divided by') + - ":": [T: "dois pontos"] # 0x3a (en: 'colon') + - ";": [T: "ponto e vírgula"] # 0x3b (en: 'semicolon') + - "<": # 0x3c + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] # (en: 'is', google translation) + - T: "menor que" # (en: 'less than') + - "=": # 0x3d + - test: + if: "$Verbosity!='Terse'" + then: [T: "é igual a"] # (en: 'is equal to', google translation) + else: [T: "igual a"] # (en: 'equals', google: 'iguales') + + - ">": # 0x3e + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] # (en: 'is', google translation) + - T: "maior que" # (en: 'greater than') + - "?": [T: "ponto de interrogação"] # 0x3f (en: 'question mark', MathPlayer: 'cierra interrogación', google: 'signo de interrogación') + - "@": [T: "arroba"] # 0x40 (en: 'at sign') + - "[": # 0x5b + - test: + if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' + then: [T: "abre colchetes"] # (en: 'open bracket', google translation) + else: [T: "abre colchetes"] # (en: 'left bracket', MathPlayer: 'abre corchetes', google: 'soporte izquierdo') + - "\\": [T: "barra invertida"] # 0x5c (en: 'back slash', MathPlayer: 'abre corchetes', google: 'back slash') + - "]": # 0x5d + - test: + if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' + then: [T: "fecha colchetes"] # (en: 'close bracket', google translation) + else: [T: "fecha colchetes"] # (en: 'right bracket', MathPlayer: 'cierra corchetes', google: 'soporte derecho') + # NOTA: o inglês distingue 'hat' (ornamento sobre variável) de 'caret' + # (o símbolo isolado). O português usa "circunflexo" nos dois casos, então + # o teste contextual seria redundante e foi omitido de propósito. + - "^": [T: "circunflexo"] # 0x5e (en: 'hat' / 'caret') + - "_": [T: "sublinhado"] # 0x5f (en: 'under bar') + - "`": [T: "acento grave"] # 0x60 (en: 'grave') + - "{": # 0x7b + - test: + if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' + then: [T: "abre chaves"] # (en: 'open brace', google translation) + else: [T: "abre chaves"] # (en: 'left brace', MathPlayer: 'abre llaves', google: 'abrazadera izquierda') + - "|": # 0x7c + # note: for ClearSpeak and SimpleSpeak, "|" inside of sets is handled at the mrow level, same for 'sets' + # CORREÇÃO: a versão anterior era uma simplificação da regra inglesa e perdia + # três casos: P(A|B) ("dado"), a|b entre números ("divide") e o intent literal. + # Fora do ClearSpeak tudo caía em "barra vertical". + - 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: "barra vertical"] # (en: 'vertical line') + - else_if: "$SpeechStyle != 'ClearSpeak'" + then_test: + - if: "$DefaultToGiven" + then: [T: "dado"] # (en: 'given') + - 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: "divide"] # (en: 'divides') + else: [T: "barra vertical"] # (en: 'vertical line') + - else_if: "not(preceding-sibling::*) or not(following-sibling::*)" + then: [T: "barra vertical"] # (en: 'vertical line') + - else_if: "$ClearSpeak_VerticalLine = 'SuchThat'" + then: [T: "tal que"] # (en: 'such that') + - else_if: "$ClearSpeak_VerticalLine = 'Given' or $DefaultToGiven" + then: [T: "dado"] # (en: 'given') + - else: [T: "divide"] # (en: 'divides') + + - "}": # 0x7d + - test: + if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' + then: [T: "fecha chaves"] # (en: 'close brace', google translation) + else: [T: "fecha chaves"] # (en: 'right brace', MathPlayer: 'cierra llaves', google: 'abrazadera derecha') + + - "~": [T: "til"] # 0x7e + - " ": # 0xa0 + - test: + if: "@data-empty-in-2D" + then: [T: "vazio"] # want to say something for fraction (etc) with empty child (en: 'empty', google translation) + else: [T: ""] + + - "¢": [T: "centavos"] # 0xa2 (en: 'cents') + - "£": [T: "libras"] # 0xa3 (en: 'pounds') + - "¬": [T: "não"] # 0xac (en: 'not') + - "°": [T: "graus"] # 0xb0 (en: 'degrees') + - "±": [T: "mais ou menos"] # 0xb1 (en: 'plus or minus') + - "´": [T: "acento agudo"] # 0xb4 (en: 'acute') + - "·": # 0xB7 + - test: + if: "$SpeechStyle != 'ClearSpeak' or $ClearSpeak_MultSymbolDot = 'Auto'" + then_test: + if: "$Verbosity = 'Verbose'" + then: [T: "multiplicado por"] # (en: 'times', google translation) + else: [T: "vezes"] + else: [T: "vezes"] # (en: 'dot') + - "×": # 0xd7 + - test: + if: "$Verbosity = 'Verbose'" + then: [T: "multiplicado por"] # (en: 'times', google translation) + else: [T: "vezes"] + - "÷": [T: "dividido por"] # 0xf7 (en: 'divided by') + - "̀": [T: "acento grave"] # 0x300 (en: 'grave accent embellishment', google: 'embellido de acento grave') + - "́": [T: "com acento agudo"] # 0x301 (en: 'acute accent embellishment', google: 'embellido de acento agudo') + - "̂": [T: "com acento circunflexo"] # 0x302 (en: 'circumflex accent embellishment', google: 'embellamiento del acento circunflejo') + - "̃": [T: "til"] # 0x303 (en: 'tilde embellishment', google: 'tilde adorno') + - "̄": [T: "com mácron"] # 0x304 (en: 'macron embellishment', google: 'embellido de macron') + - "̅": [T: "com barra acima"] # 0x305 (en: 'overbar embellishment', google: 'embellido de overbar') + - "̆": [T: "breve"] # 0x306 + - "̇": [T: "ponto acima"] # 0x307 (en: 'dot above embellishment', google: 'punto por encima del adorno') + + # 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: "maiúscula"] # (en: 'cap', google translation) + 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: "alfa"] # 0x3b1 (en: 'alpha') + - "β": [T: "beta"] # 0x3b2 + - "γ": [T: "gama"] # 0x3b3 (google: 'gama') + - "δ": [T: "delta"] # 0x3b4 + - "ε": [T: "épsilon"] # 0x3b5 (en: 'epsilon', google: 'epsilon') + - "ζ": [T: "zeta"] # 0x3b6 + - "η": [T: "eta"] # 0x3b7 + - "θ": [T: "teta"] # 0x3b8 + - "ι": [T: "iota"] # 0x3b9 + - "κ": [T: "capa"] # 0x3ba + - "λ": [T: "lambda"] # 0x3bb + - "μ": [T: "mi"] # 0x3bc (en: 'mu', google: 'mu') + - "ν": [T: "ni"] # 0x3bd (en: 'nu', google: 'nu') + - "ξ": [T: "csi"] # 0x3be (en: 'zai', google: 'zai') + - "ο": [T: "ômicron"] # 0x3bf (en: 'omicron', google: 'omicron') + - "π": [T: "pi"] # 0x3c0 + - "ρ": [T: "rô"] # 0x3c1 + - "ς": [T: "sigma final"] # 0x3c2 (en: 'final sigma', google: 'final sigma') + - "σ": [T: "sigma"] # 0x3c3 + - "τ": [T: "tau"] # 0x3c4 + - "υ": [T: "úpsilon"] # 0x3c5 (en: 'upsilon', google: 'upsilon') + - "φ": [T: "fi"] # 0x3c6 (google: 'fi') + - "χ": [T: "qui"] # 0x3c7 (en: 'chi', google: 'chi') + - "ψ": [T: "psi"] # 0x3c8 + - "ω": [T: "ômega"] # 0x3c9 + - "ϕ": [T: "fi reto"] # 0x3d5 (en: 'phi', google: 'fi') + - "ϖ": [T: "pi variante"] # 0x3d6 (en: 'pi', google: 'pi') + - "ϵ": [T: "épsilon variante"] # 0x3f5 (en: 'epsilon', google: 'epsilon') + - "϶": [T: "épsilon invertido"] # 0x3f6 (en: 'reversed epsilon') + + - "⁢": [T: ""] # 0x2062 + - "ℂℕℚℝℤ": # here we rely on this running through the table again to speak "cap xxx" + # Adjetivo de estilo POSPOSTO (decisão 8.1): o spell: vem primeiro. + - spell: "translate('.', 'ℂℕℚℝℤ', 'CNQRZ')" + - T: "vazado" # (en: 'double-struck', google: 'doble golpe') + + - "℃": [T: "graus Celsius"] # 0x2103 (en: 'degrees celsius', google: 'grados celsius') + - "ℋℛℓ": # 0x210b + # 'caligráfico', não 'cursivo': termo único da família script, igual ao + # que o unicode-full.yaml usa (ℐ ℒ ℘ ℬ ℰ ℱ ℳ). t: minúsculo porque a + # escolha entre os dois ainda depende de escuta — ver ACHADOS 7.8. + - spell: "translate('.', 'ℋℛℓ', 'HRl')" + - t: "caligráfico" # (en: 'script', google: 'guion') + - "ℜ": # 0x211c + # 'fraktur', não 'gótico': termo único da família, igual ao + # unicode-full.yaml (ℑ ℌ ℨ ℭ e os blocos do plano 1). t: minúsculo. + - spell: "'R'" + - t: "fraktur" # (en: 'fraktur', google: 'fraktur') + + - "ⅆⅇⅈⅉ": # 0x2146-9 + - spell: "translate('.', 'ⅆⅇⅈⅉ', 'deij')" + - T: "vazado itálico" # (en: 'double-struck italic') + + - "∥": # 0x2225 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] # (en: 'is', google translation) + - T: "paralela a" # (en: 'parallel to') + - "∦": # 0x2226 + # audit-ignore: em português a negação já é frase completa; o "is" do inglês + # não tem par que valha suprimir no Terse. Com o ramo "é" a fala saía + # "é não é paralela a". Mesmo tratamento que ≢, ≇, ⊈ e ⊉. + - T: "não é paralela a" # (en: 'not parallel to') + - "≤": # 0x2264 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] # (en: 'is', google translation) + - T: "menor ou igual a" + - "≥": # 0x2265 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] # (en: 'is', google translation) + - T: "maior ou igual a" # (en: 'greater than or equal to') + + # --- 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: "reticências"] # (en: 'dot dot dot', google translation) + else_test: # must have $ClearSpeak_Ellipses = 'AndSoOn' + if: "count(following-sibling::*) = 0" + then: [T: "etcétera"] # (en: 'and so on', google translation) + else: [T: "reticências"] # (en: 'and so on up to', MathPlayer: 'punto punteado punteado', google: 'y así sucesivamente') + - "⁡": # 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: "de"] # (en: 'of', google translation) + - "→": # 0x2192 + - test: + if: "ancestor::*[2][self::m:limit]" + then: [T: "tende a"] # (en: 'approaches') + else: [T: "seta para a direita"] # (en: 'right arrow', MathPlayer: 'flecha', google: 'flecha correcta') + - "∂": # 0x2202 + - test: + if: "$Verbosity='Terse'" + then: [T: "parcial"] # (en: 'partial', google translation) + else: [T: "derivada parcial"] # (en: 'partial derivative') + - "∆": # 0x2206 + - test: + if: "$Verbosity!='Terse'" + then: [T: "o"] # (en: 'the', google translation) + - T: "incremento" # (en: 'laplacian of') + - "∈": # 0x2208 + - test: + if: "$SpeechStyle != 'ClearSpeak'" + # Também aqui o ramo dentro de um conjunto precisa do particípio: uma + # string só para os dois contextos daria "o conjunto de todos os x + # pertence a inteiros", com o verbo grudando em "conjunto". + then_test: + if: "../../self::m:set or ../../../self::m:set" + then: [T: "pertencentes a"] + else: [T: "pertence a"] # (en: 'an element of'; uso brasileiro corrente) + # 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: + # DENTRO de um conjunto a expressão modifica "todos os x": nada de + # verbo finito (grudaria em "conjunto") e concordância no plural. + # É o que o inglês faz com 'in / member of / element of / belonging to'. + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "em"] # (en: 'in', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "membros de"] # (en: 'member of'; plural concorda com "todos os x") + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "elementos de"] # (en: 'element of'; plural concorda com "todos os x") + - else: [T: "pertencentes a"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'belonging to') + else_test: + # Auto separado de Member, como o ∉ já tinha: com as preferências + # padrão ($ClearSpeak_SetMemberSymbol = 'Auto') o brasileiro + # corrente é "pertence a". Ver ACHADOS 7.8. + - if: $ClearSpeak_SetMemberSymbol = 'Auto' + then: [T: "pertence a"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "é membro de"] # (en: 'is a member of', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "é um elemento de"] # (en: 'is an element of', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "está em"] # (en: 'is in', google translation) + - else: [T: "pertence a"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'belongs to') + - "∉": # 0x2209 + # rule is identical to 0x2208 + - test: + if: "$SpeechStyle != 'ClearSpeak'" + then_test: + if: "../../self::m:set or ../../../self::m:set" + then: [T: "não pertencentes a"] + else: [T: "não pertence a"] # (en: 'is not an element of'; uso brasileiro) + # 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: + # Idem ao 0x2208: sem verbo finito, concordando com "todos os x". + # "não em" é truncado ("não" não nega preposição nua) e "fora de" + # troca o conceito (afirma localização em vez de negar pertinência, + # ambíguo com complementar). Fica a forma nominal da negação, que + # coincide com o ramo Belongs — decisão registrada em + # TERMINOLOGIA_PT_BR.md e ACHADOS 7.9. + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "não pertencentes a"] # (en: 'not in') + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "não membros de"] # (en: 'not member of') + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "não elementos de"] # (en: 'not element of') + - else: [T: "não pertencentes a"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'not belonging to') + else_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' + then: [T: "não pertence a"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "não é membro de"] # (en: 'is not a member of') + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "não é um elemento de"] # (en: 'is not an element of', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + # Era "não está contido em", que é exatamente a fala do ⊄ + # (ver 0x2284): duas relações distintas soavam iguais. Agora + # espelha o "está em" do ∈ e o que o próprio ∉ diz num conjunto. + then: [T: "não está em"] # (en: 'is not in', google translation) + - else: [T: "não pertence a"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'does not belong to') + - "∊": # 0x220a + # variante de forma do 0x2208: fala igual ao ∈ em todos os ramos. + - test: + if: "$SpeechStyle != 'ClearSpeak'" + then_test: + if: "../../self::m:set or ../../../self::m:set" + then: [T: "pertencentes a"] + else: [T: "pertence a"] # alinhado ao ∈ (era "é um elemento de") + # 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: + # DENTRO de um conjunto a expressão modifica "todos os x": nada de + # verbo finito (grudaria em "conjunto") e concordância no plural. + # É o que o inglês faz com 'in / member of / element of / belonging to'. + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "em"] # (en: 'in', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "membros de"] # (en: 'member of'; plural concorda com "todos os x") + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "elementos de"] # (en: 'element of'; plural concorda com "todos os x") + - else: [T: "pertencentes a"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'belonging to') + else_test: + # Auto separado de Member, igual ao ∈ e ao ∉. Ver ACHADOS 7.8. + - if: $ClearSpeak_SetMemberSymbol = 'Auto' + then: [T: "pertence a"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "é membro de"] # (en: 'is a member of', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "é um elemento de"] # (en: 'is an element of', google translation) + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "está em"] # (en: 'is in', google translation) + - else: [T: "pertence a"] # $ClearSpeak_SetMemberSymbol = 'Belongs' (en: 'belongs to') + - "√": # 0x221a + - test: + if: "$Verbosity!='Terse'" + then: [T: "a"] # (en: 'the', google translation) + - T: "raiz quadrada de" # (en: 'square root of') + - "∝": # 0x221d + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] # (en: 'is', google translation) + - T: "proporcional a" # (en: 'proportional to') + - "∶": # 0x2236 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] # (en: 'is', google translation) + - T: "a" # (en: 'to') + - "∾": # 0x223e + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] # (en: 'is', google translation) + - T: "mais positivo" # (en: 'most positive') + - "≠": # 0x2260 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # ("diferente de" já dispensa a cópula) + - T: "diferente de" # (en: 'not equal to'; MathPlayer sugeria 'distinto de' e o algoritmo escolheu a do Google) + - "≡": # 0x2261 + - test: + if: "$Verbosity!='Terse'" + then: [T: "é"] # (en: 'is', google translation) + - T: "idêntico a" # (en: 'identical to') + - "⊂": # 0x2282 + - test: + if: "$Verbosity!='Terse'" + then: [T: "está"] # (en: 'is a'; "está contido em" é o uso brasileiro) + - T: "contido em" # (en: 'subset of') + - "⊃": # 0x2283 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # (en: 'is a'; "contém" já é verbo) + - T: "contém" # (en: 'superset of') + - "⊄": # 0x2284 + - test: + if: "$Verbosity!='Terse'" + then: [T: "não"] # (coerente com ⊂: "não está contido em") + - T: "está contido em" # (en: 'not a subset of') + - "⊅": # 0x2285 + - test: + if: "$Verbosity!='Terse'" + then: [T: "não"] # (coerente com ⊃) + - T: "contém" # (en: 'not a superset of') + - "⊆": # 0x2286 + - test: + if: "$Verbosity!='Terse'" + then: [T: "está"] # (coerente com ⊂) + - T: "contido em ou é igual a" # (en: 'subset of or equal to') + - "⊇": # 0x2287 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # (coerente com ⊃) + - T: "contém ou é igual a" # (en: 'superset of or equal to') + + # --- restored short definitions that differ from unicode-full --- + - "–": [T: "meia-risca"] # 0x2013 (en: 'en dash') + - "—": [T: "travessão"] # 0x2014 (en: 'em dash') + - "‖": [T: "barra vertical dupla"] # 0x2016 (en: 'double vertical line') + - "″": [T: "duas linhas"] # 0x2033 (en: 'double prime', MathPlayer: 'segunda', google: 'doble primo') + - "‴": [T: "três linhas"] # 0x2034 (en: 'triple prime', MathPlayer: 'tercera', google: 'triple prime') + - "‵": [T: "linha invertida"] # 0x2035 (en: 'reversed prime') + - "‶": [T: "duas linhas invertidas"] # 0x2036 (en: 'reversed double prime') + - "‷": [T: "três linhas invertidas"] # 0x2037 (en: 'reversed triple prime', google: 'triple prime invertido') + - "℉": [T: "graus Fahrenheit"] # 0x2109 (en: 'degrees fahrenheit') + - "ℎ": [T: "constante de Planck"] # 0x210e (en: 'planck constant', MathPlayer: 'constante de Planck', google: 'planck constante') + - "K": [T: "kelvin"] # 0x212a (en: 'kelvin') + - "←": [T: "seta para a esquerda"] # 0x2190 (en: 'leftwards arrow') + - "↑": [T: "seta para cima"] # 0x2191 (en: 'upwards arrow') + - "↓": [T: "seta para baixo"] # 0x2193 (en: 'downwards arrow') + - "⇒": [T: "implica que"] # 0x21d2 (en: 'rightwards double arrow') + - "∘": [T: "composto com"] # 0x2218 (en: 'composed with') + - "∣": # 0x2223 (en: 'divides') + # O latex2mathml emite \mid como U+2223, não como "|". Em P(A ∣ B) é + # "dado" — mesma condição de contexto da regra do "|" (0x7c). Fora disso, + # continua "divide" (a ∣ b entre inteiros). Ver ACHADOS 9.6. + - test: + if: "count(preceding-sibling::*)=1 and count(following-sibling::*)=1 and ../../../*[1][.='P']" + then: [T: "dado"] # (en: 'given') + else: [T: "divide"] + - "∤": [T: "não divide"] # 0x2224 (en: 'does not divide') + - "∷": [T: "como"] # 0x2237 (en: 'as') + - "∼": [T: "varia com"] # 0x223c (en: 'varies with') + - "∽": [T: "til invertido"] # 0x223d (en: 'reversed tilde') + - "∿": [T: "onda senoidal"] # 0x223f (en: 'sine wave') + - "≦": [T: "menor ou igual a"] # 0x2266 (en: 'less than over equal to') + - "≧": [T: "maior ou igual a"] # 0x2267 (en: 'greater than over equal to') + +# --- ACRÉSCIMOS pt (não existiam no es e caíam no unicode-full em inglês) --- + - "≪": [T: "é muito menor que"] # 0x226a (en: 'much less than') + - "≫": [T: "é muito maior que"] # 0x226b (en: 'much greater than') + - "⊥": [T: "é perpendicular a"] # 0x22a5 (en: 'perpendicular to') +# Ornamentos usados em variáveis modificadas (x com trema = 2a derivada): + - "̌": [T: "cáron"] # 0x030c (en: 'caron embellishment') + - "̈": [T: "dois pontos"] # 0x0308 (en: 'double dot above') + - "⃛": [T: "três pontos acima"] # 0x20db (en: 'triple dot above') + - "⃜": [T: "quatro pontos acima"] # 0x20dc (en: 'quadruple dot above') + - "⁤": [T: "e"] # 0x2064 (invisible plus — "dois e um meio"); FALTAVA em pt + - "∐": [T: "coproduto"] # 0x2210 (en: 'co-product'); FALTAVA em pt + - "⁣": [T: ""] # 0x2063 (invisible separator — silencioso) + - "′": [T: "linha"] # 0x2032 (en: 'prime') + - "↗": # 0x2197 + - test: + if: "ancestor::*[2][self::m:limit]" + then: [T: "tende por baixo a"] + else: [T: "seta para nordeste"] + - "↘": # 0x2198 + - test: + if: "ancestor::*[2][self::m:limit]" + then: [T: "tende por cima a"] + else: [T: "seta para sudeste"] + - "˙": [T: "ponto"] # 0x02d9 (dot above) + - "¨": [T: "dois pontos"] # 0x00a8 (diaeresis / 2a derivada) diff --git a/tests/Languages/pt.rs b/tests/Languages/pt.rs new file mode 100644 index 000000000..dac447ebd --- /dev/null +++ b/tests/Languages/pt.rs @@ -0,0 +1,17 @@ +#![allow(non_snake_case)] +// Módulo de testes do idioma pt (português). +// IMPORTANTE: acrescente `mod pt;` em tests/languages.rs para ativar. +mod ClearSpeak { + mod sets; +} +mod SimpleSpeak { + mod functions; +} +mod shared; +mod chemistry; +mod alphabets; +mod intent; +mod units; +mod unicode_full; +mod escuta; // gerador do pacote de escuta (#[ignore]) + diff --git a/tests/Languages/pt/ClearSpeak/sets.rs b/tests/Languages/pt/ClearSpeak/sets.rs new file mode 100644 index 000000000..ff2ee45cd --- /dev/null +++ b/tests/Languages/pt/ClearSpeak/sets.rs @@ -0,0 +1,398 @@ +/// Testes de conjuntos e pertinência — pt-BR, ClearSpeak e SimpleSpeak. +/// +/// POR QUE ESTE ARQUIVO EXISTE +/// O par ∈ / ∊ falou "é membro de" com as preferências padrão durante duas +/// rodadas inteiras sem ninguém notar, porque o pt não tinha NENHUM teste de +/// pertinência — nem ClearSpeak, nem SimpleSpeak. O ∉ foi corrigido sozinho +/// numa rodada e os outros dois ficaram para trás. Ver ACHADOS 7.8. +/// +/// A invariante que este arquivo trava: **∈ (0x2208) e ∊ (0x220a) são +/// variantes de forma do mesmo símbolo e devem falar igual em todos os ramos** +/// — nas cinco preferências de ClearSpeak_SetMemberSymbol, dentro e fora de +/// um conjunto, nos dois estilos de fala. +/// +/// COMO AS EXPECTATIVAS FORAM ESCRITAS +/// Todas saíram da saída real do motor, nunca de tradução do inglês. O método +/// é o do cabeçalho de tests/Languages/pt/shared.rs: rodar, comparar `left` +/// com `right`, e se o gerado estiver certo em português, copiá-lo; se estiver +/// errado, consertar a REGRA, nunca o teste. +/// +/// Cada teste de pertinência fixa ClearSpeak_SetMemberSymbol explicitamente, +/// inclusive no caso 'Auto'. Isso é de propósito: as preferências de +/// ClearSpeak não são reinicializadas entre testes na mesma thread, então um +/// teste que dependesse do padrão implícito poderia passar ou falhar conforme +/// a ordem de execução. + +use crate::common::*; +use anyhow::Result; + +// x ℝ — pertinência FORA de um conjunto (ramo else_test das regras) +fn sozinho(simbolo: &str) -> String { + format!("x{simbolo}ℝ") +} + +// { x ℤ : x > 5 } — pertinência DENTRO de um conjunto (ramo then_test) +fn no_conjunto(simbolo: &str) -> String { + format!(" + {{ + x {simbolo} ℤ + : x > 5 + }} + ") +} + +const PERTENCE: &str = "\u{2208}"; // ∈ +const NAO_PERTENCE: &str = "\u{2209}"; // ∉ +const PERTENCE_PEQ: &str = "\u{220A}"; // ∊ — variante de forma do 0x2208 + +/// ∈ e ∊ têm de falar igual. É o caso que sobreviveu duas rodadas. +fn ambas_variantes(pref: &str, expr: fn(&str) -> String, esperado: &str) -> Result<()> { + test_ClearSpeak("pt", "ClearSpeak_SetMemberSymbol", pref, &expr(PERTENCE), esperado)?; + test_ClearSpeak("pt", "ClearSpeak_SetMemberSymbol", pref, &expr(PERTENCE_PEQ), esperado)?; + return Ok(()); +} + +// --------------------------------------------------------------------------- +// Conjuntos numéricos +// --------------------------------------------------------------------------- + +#[test] +fn complexos() -> Result<()> { + test("pt", "ClearSpeak", "ℂ", "números complexos")?; + return Ok(()); +} + +#[test] +fn naturais() -> Result<()> { + test("pt", "ClearSpeak", "ℕ", "números naturais")?; + return Ok(()); +} + +#[test] +fn racionais() -> Result<()> { + test("pt", "ClearSpeak", "ℚ", "números racionais")?; + return Ok(()); +} + +#[test] +fn reais() -> Result<()> { + test("pt", "ClearSpeak", "ℝ", "números reais")?; + return Ok(()); +} + +// Sem "números", diferente dos quatro acima. O inglês tem a mesma assimetria +// ("the integers" contra "the complex numbers"), então isto acompanha o en. +#[test] +fn inteiros() -> Result<()> { + test("pt", "ClearSpeak", "ℤ", "inteiros")?; + return Ok(()); +} + +#[test] +fn conjuntos_numericos_iguais_em_simplespeak() -> Result<()> { + test("pt", "SimpleSpeak", "ℂ", "números complexos")?; + test("pt", "SimpleSpeak", "ℕ", "números naturais")?; + test("pt", "SimpleSpeak", "ℚ", "números racionais")?; + test("pt", "SimpleSpeak", "ℝ", "números reais")?; + test("pt", "SimpleSpeak", "ℤ", "inteiros")?; + return Ok(()); +} + +// --------------------------------------------------------------------------- +// Conjunto vazio, por extensão, por compreensão +// --------------------------------------------------------------------------- + +#[test] +fn conjunto_vazio_por_chaves() -> Result<()> { + test("pt", "ClearSpeak", "{ }", "o conjunto vazio")?; + test("pt", "SimpleSpeak", "{ }", "o conjunto vazio")?; + return Ok(()); +} + +// Sem artigo, ao contrário das chaves vazias acima. O en faz a mesma distinção +// ('empty set' para ∅, 'the empty set' para {}), então isto acompanha o en. +#[test] +fn conjunto_vazio_por_simbolo() -> Result<()> { + test("pt", "ClearSpeak", "∅", "conjunto vazio")?; + test("pt", "SimpleSpeak", "∅", "conjunto vazio")?; + return Ok(()); +} + +#[test] +fn extensao_um_elemento() -> Result<()> { + let expr = "{ 12 }"; + test("pt", "ClearSpeak", expr, "o conjunto 12")?; + test("pt", "SimpleSpeak", expr, "o conjunto 12")?; + return Ok(()); +} + +#[test] +fn extensao_varios_elementos() -> Result<()> { + let expr = " + { 5 , 10 , 15 } + "; + test("pt", "ClearSpeak", expr, "o conjunto 5 vírgula, 10 vírgula, 15")?; + test("pt", "SimpleSpeak", expr, "o conjunto 5 vírgula, 10 vírgula, 15")?; + return Ok(()); +} + +#[test] +fn compreensao_com_dois_pontos() -> Result<()> { + let expr = " + { x:x>2 } + "; + test("pt", "ClearSpeak", expr, "o conjunto de todos os x tal que x é maior que 2")?; + return Ok(()); +} + +// A barra vertical tem de falar igual aos dois pontos. +#[test] +fn compreensao_com_barra() -> Result<()> { + let expr = " + { x|x>2 } + "; + test("pt", "ClearSpeak", expr, "o conjunto de todos os x tal que x é maior que 2")?; + test("pt", "SimpleSpeak", expr, "o conjunto de todos os x tal que x é maior que 2")?; + return Ok(()); +} + +// --------------------------------------------------------------------------- +// União e interseção +// --------------------------------------------------------------------------- + +#[test] +fn uniao() -> Result<()> { + let expr = "A∪B"; + test("pt", "ClearSpeak", expr, "maiúscula a união maiúscula b")?; + test("pt", "SimpleSpeak", expr, "maiúscula a união maiúscula b")?; + return Ok(()); +} + +#[test] +fn intersecao() -> Result<()> { + let expr = "A∩B"; + test("pt", "ClearSpeak", expr, "maiúscula a interseção maiúscula b")?; + test("pt", "SimpleSpeak", expr, "maiúscula a interseção maiúscula b")?; + return Ok(()); +} + +// --------------------------------------------------------------------------- +// Pertinência FORA de um conjunto — as cinco preferências +// --------------------------------------------------------------------------- + +#[test] +fn pertinencia_auto() -> Result<()> { + ambas_variantes("Auto", sozinho, "x pertence a números reais") +} + +#[test] +fn pertinencia_member() -> Result<()> { + ambas_variantes("Member", sozinho, "x é membro de números reais") +} + +#[test] +fn pertinencia_element() -> Result<()> { + ambas_variantes("Element", sozinho, "x é um elemento de, números reais") +} + +#[test] +fn pertinencia_belongs() -> Result<()> { + ambas_variantes("Belongs", sozinho, "x pertence a números reais") +} + +#[test] +fn pertinencia_in() -> Result<()> { + ambas_variantes("In", sozinho, "x está em números reais") +} + +// --------------------------------------------------------------------------- +// Pertinência DENTRO de um conjunto — as cinco preferências +// --------------------------------------------------------------------------- + +#[test] +fn pertinencia_no_conjunto_auto() -> Result<()> { + ambas_variantes("Auto", no_conjunto, + "o conjunto de todos os x em inteiros tal que x é maior que 5") +} + +#[test] +fn pertinencia_no_conjunto_member() -> Result<()> { + ambas_variantes("Member", no_conjunto, + "o conjunto de todos os x membros de inteiros tal que x é maior que 5") +} + +#[test] +fn pertinencia_no_conjunto_element() -> Result<()> { + ambas_variantes("Element", no_conjunto, + "o conjunto de todos os x elementos de inteiros tal que x é maior que 5") +} + +// Auto e In coincidem dentro de um conjunto, como no inglês ('Auto or In'). +#[test] +fn pertinencia_no_conjunto_in() -> Result<()> { + ambas_variantes("In", no_conjunto, + "o conjunto de todos os x em inteiros tal que x é maior que 5") +} + +// --------------------------------------------------------------------------- +// Não-pertinência +// --------------------------------------------------------------------------- + +#[test] +fn nao_pertinencia_auto() -> Result<()> { + test_ClearSpeak("pt", "ClearSpeak_SetMemberSymbol", "Auto", + &sozinho(NAO_PERTENCE), "x não pertence a números reais")?; + return Ok(()); +} + +#[test] +fn nao_pertinencia_member() -> Result<()> { + test_ClearSpeak("pt", "ClearSpeak_SetMemberSymbol", "Member", + &sozinho(NAO_PERTENCE), "x não é membro de, números reais")?; + return Ok(()); +} + +#[test] +fn nao_pertinencia_element() -> Result<()> { + test_ClearSpeak("pt", "ClearSpeak_SetMemberSymbol", "Element", + &sozinho(NAO_PERTENCE), "x não é um elemento de, números reais")?; + return Ok(()); +} + +#[test] +fn nao_pertinencia_belongs() -> Result<()> { + test_ClearSpeak("pt", "ClearSpeak_SetMemberSymbol", "Belongs", + &sozinho(NAO_PERTENCE), "x não pertence a números reais")?; + return Ok(()); +} + +// Auto e In coincidem na negação também, como no positivo. E aqui coincidem +// também com Belongs: "não em" é truncado e "fora de" troca o conceito +// (localização, não pertinência), então a negação nominal é uma só. +// Ver TERMINOLOGIA_PT_BR.md. +#[test] +fn nao_pertinencia_no_conjunto_auto() -> Result<()> { + test_ClearSpeak("pt", "ClearSpeak_SetMemberSymbol", "Auto", &no_conjunto(NAO_PERTENCE), + "o conjunto de todos os x não pertencentes a inteiros tal que x é maior que 5")?; + return Ok(()); +} + +#[test] +fn nao_pertinencia_no_conjunto_member() -> Result<()> { + test_ClearSpeak("pt", "ClearSpeak_SetMemberSymbol", "Member", &no_conjunto(NAO_PERTENCE), + "o conjunto de todos os x não membros de inteiros tal que x é maior que 5")?; + return Ok(()); +} + +#[test] +fn nao_pertinencia_no_conjunto_element() -> Result<()> { + test_ClearSpeak("pt", "ClearSpeak_SetMemberSymbol", "Element", &no_conjunto(NAO_PERTENCE), + "o conjunto de todos os x não elementos de inteiros tal que x é maior que 5")?; + return Ok(()); +} + +#[test] +fn nao_pertinencia_no_conjunto_in() -> Result<()> { + test_ClearSpeak("pt", "ClearSpeak_SetMemberSymbol", "In", &no_conjunto(NAO_PERTENCE), + "o conjunto de todos os x não pertencentes a inteiros tal que x é maior que 5")?; + return Ok(()); +} + +// --------------------------------------------------------------------------- +// SimpleSpeak — a preferência de ClearSpeak não vale aqui +// --------------------------------------------------------------------------- + +#[test] +fn pertinencia_simplespeak() -> Result<()> { + test("pt", "SimpleSpeak", &sozinho(PERTENCE), "x pertence a números reais")?; + test("pt", "SimpleSpeak", &sozinho(PERTENCE_PEQ), "x pertence a números reais")?; + return Ok(()); +} + +#[test] +fn nao_pertinencia_simplespeak() -> Result<()> { + test("pt", "SimpleSpeak", &sozinho(NAO_PERTENCE), "x não pertence a números reais")?; + return Ok(()); +} + +// --------------------------------------------------------------------------- +// Pertinência sob um operador grande (não é "dentro de um conjunto") +// --------------------------------------------------------------------------- + +#[test] +fn pertinencia_sob_somatorio() -> Result<()> { + let expr = " + + ∑ + i ∈ ℤ + + + 1 + i 2 + + "; + test_ClearSpeak("pt", "ClearSpeak_SetMemberSymbol", "Auto", expr, + "o somatório sobre i pertence a inteiros de; a fração com numerador 1; e denominador i ao quadrado")?; + return Ok(()); +} + +// --------------------------------------------------------------------------- +// Ramos que exigem forma não-finita (antes falavam com verbo finito) +// +// Dentro de um conjunto a expressão modifica "todos os x". Verbo finito ali +// gruda em "conjunto" ("o conjunto de todos os x PERTENCE A inteiros") e a +// concordância cai no lugar errado. O inglês usa 'belonging to' justamente +// para evitar isso. Ver ACHADOS 7.9. +// --------------------------------------------------------------------------- + +#[test] +fn pertinencia_no_conjunto_belongs() -> Result<()> { + ambas_variantes("Belongs", no_conjunto, + "o conjunto de todos os x pertencentes a inteiros tal que x é maior que 5") +} + +#[test] +fn nao_pertinencia_no_conjunto_belongs() -> Result<()> { + test_ClearSpeak("pt", "ClearSpeak_SetMemberSymbol", "Belongs", &no_conjunto(NAO_PERTENCE), + "o conjunto de todos os x não pertencentes a inteiros tal que x é maior que 5")?; + return Ok(()); +} + +// O SimpleSpeak usava UMA string só para dentro e fora do conjunto, então não +// conseguia distinguir os dois contextos. Agora bifurca como o ClearSpeak. +#[test] +fn pertinencia_no_conjunto_simplespeak() -> Result<()> { + test("pt", "SimpleSpeak", &no_conjunto(PERTENCE), + "o conjunto de todos os x pertencentes a inteiros tal que x é maior que 5")?; + test("pt", "SimpleSpeak", &no_conjunto(PERTENCE_PEQ), + "o conjunto de todos os x pertencentes a inteiros tal que x é maior que 5")?; + return Ok(()); +} + +#[test] +fn nao_pertinencia_no_conjunto_simplespeak() -> Result<()> { + test("pt", "SimpleSpeak", &no_conjunto(NAO_PERTENCE), + "o conjunto de todos os x não pertencentes a inteiros tal que x é maior que 5")?; + return Ok(()); +} + +/// Fora de um conjunto o verbo finito está certo (o sujeito é um termo só), mas +/// o ∉ com 'In' falava "não está contido em" — exatamente a fala do ⊄ +/// (unicode.yaml, 0x2284). Duas relações distintas soavam idênticas. Agora +/// espelha o "está em" do ∈, que é o que o próprio ∉ já dizia num conjunto. +#[test] +fn nao_pertinencia_in_nao_colide_com_subconjunto() -> Result<()> { + test_ClearSpeak("pt", "ClearSpeak_SetMemberSymbol", "In", + &sozinho(NAO_PERTENCE), "x não está em números reais")?; + return Ok(()); +} + +/// Guarda da distinção: ∉ e ⊄ não podem falar igual. +#[test] +fn nao_pertinencia_e_nao_subconjunto_falam_diferente() -> Result<()> { + test_ClearSpeak("pt", "ClearSpeak_SetMemberSymbol", "In", + "x\u{2209}ℝ", "x não está em números reais")?; + test("pt", "ClearSpeak", + "x\u{2284}ℝ", "x não está contido em, números reais")?; + return Ok(()); +} diff --git a/tests/Languages/pt/SimpleSpeak/functions.rs b/tests/Languages/pt/SimpleSpeak/functions.rs new file mode 100644 index 000000000..a656c2da0 --- /dev/null +++ b/tests/Languages/pt/SimpleSpeak/functions.rs @@ -0,0 +1,422 @@ +/// TESTES DO IDIOMA pt — como reconciliar com as regras +/// +/// As strings esperadas abaixo foram escritas para refletir a intenção +/// das regras em Rules/Languages/pt. Vírgulas e ponto-e-vírgula marcam +/// PAUSAS inseridas pelo motor e podem divergir levemente na primeira +/// execução. Método do guia oficial de tradutores: +/// 1. rode `cargo test Languages::pt` +/// 2. para cada falha, compare `left` (esperado) com `right` (gerado) +/// 3. se o GERADO estiver correto em português, copie-o para o teste; +/// se estiver errado, conserte a REGRA, nunca o teste. +/// +/// ATENÇÃO ao histórico: o arquivo espanhol equivalente continha testes +/// que não refletiam as regras (ex.: esperava "logaritmo natural" com a +/// regra dizendo "natural log") e dano de busca-e-troca (ex.: "eigh"→"8"). +/// Não herde strings esperadas de es/ sem conferir. + +use crate::common::*; +use anyhow::Result; + +#[test] +fn trig_names() -> Result<()> { + let expr = " + sinx+ + cosy+ + tanz+ + secα+ + cscϕ+ + cotφ + "; + test("pt", "SimpleSpeak", expr, "seno de x mais cosseno de y mais tangente de z mais secante de alfa, mais cossecante de fi reto, mais cotangente de fi")?; + return Ok(()); + +} + +#[test] +fn hyperbolic_trig_names() -> Result<()> { + let expr = " + sinhx+ + coshy+ + tanhz+ + sechα+ + cschϕ+ + cothφ + "; + test("pt", "SimpleSpeak", expr, "seno hiperbólico de x, mais \ + cosseno hiperbólico de y, mais \ + tangente hiperbólica de z, mais \ + secante hiperbólica de alfa, mais, \ + cossecante hiperbólica de fi reto, mais \ + cotangente hiperbólica de fi")?; + return Ok(()); + +} + + +#[test] +fn inverse_trig() -> Result<()> { + let expr = "sin-1x"; + test("pt", "SimpleSpeak", expr, "inversa de seno de x")?; + return Ok(()); + +} + +#[test] +fn trig_squared() -> Result<()> { + let expr = "sin2x"; + test("pt", "SimpleSpeak", expr, "seno ao quadrado de x")?; + return Ok(()); + +} + +#[test] +fn trig_cubed() -> Result<()> { + let expr = "tan3x"; + test("pt", "SimpleSpeak", expr, "tangente ao cubo de x")?; + return Ok(()); + +} + +#[test] +// Reconciliado com a regra function-power corrigida: ToOrdinal() gerava +// "quarto" (masculino) contra "potência" (feminino). +fn trig_fourth() -> Result<()> { + let expr = "sec4x"; + test("pt", "SimpleSpeak", expr, "a potência 4 de, secante de x")?; + return Ok(()); + +} + + +#[test] +fn trig_power_other() -> Result<()> { + let expr = "sinh>n-1x"; + test("pt", "SimpleSpeak", expr, "a potência n menos 1 de, seno hiperbólico de x")?; + return Ok(()); + +} + +#[test] +fn simple_log() -> Result<()> { + let expr = " logx "; + test("pt", "SimpleSpeak", expr, "o log de x")?; + return Ok(()); + +} + +#[test] +fn normal_log() -> Result<()> { + let expr = "log(x+y)"; + test("pt", "SimpleSpeak", expr, "o log de, abre parênteses, x mais y, fecha parênteses")?; + return Ok(()); + +} + +#[test] +fn simple_log_with_base() -> Result<()> { + let expr = " logbx "; + test("pt", "SimpleSpeak", expr, "o log na base b, de x")?; + return Ok(()); + +} + +#[test] +fn normal_log_with_base() -> Result<()> { + let expr = "logb(x+y)"; + test("pt", "SimpleSpeak", expr, "o log na base b, de, abre parênteses, x mais y, fecha parênteses")?; + return Ok(()); + +} + +/// 'Log' com maiúscula é o logaritmo de valor principal (variável complexa) e +/// tem leitura própria, diferente de 'log'. +#[test] +fn principal_value_log() -> Result<()> { + let expr = " Logx "; + test("pt", "SimpleSpeak", expr, "o log do valor principal de x")?; + return Ok(()); + +} + +#[test] +fn simple_ln() -> Result<()> { + let expr = " lnx "; + test("pt", "SimpleSpeak", expr, "o logaritmo natural de x")?; + return Ok(()); + +} + +#[test] +fn normal_ln() -> Result<()> { + let expr = "ln(x+y)"; + test("pt", "SimpleSpeak", expr, "o logaritmo natural de, abre parênteses, x mais y, fecha parênteses")?; + return Ok(()); + +} + +#[test] +fn normal_ln_terse() -> Result<()> { + let expr = "ln(x+y)"; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Terse")], + expr, "l n, abre parênteses, x mais y, fecha parênteses")?; + return Ok(()); + +} + +#[test] +fn simple_ln_terse() -> Result<()> { + let expr = " lnx "; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Terse")], + expr, "l n x")?; + return Ok(()); + +} + +#[test] +fn explicit_function_call_with_parens() -> Result<()> { + let expr = "t⁡(x)"; + test("pt", "SimpleSpeak", expr, "t de x")?; + return Ok(()); + +} + + +#[test] +fn explicit_times_with_parens() -> Result<()> { + let expr = "t⁢(x)"; + test("pt", "SimpleSpeak", expr, "t vezes x")?; + return Ok(()); + +} + +#[test] +fn explicit_function_call() -> Result<()> { + let expr = "t⁡x"; + test("pt", "SimpleSpeak", expr, "t de x")?; + return Ok(()); + +} + +#[test] +fn explicit_times() -> Result<()> { + let expr = "t⁢x"; + test("pt", "SimpleSpeak", expr, "t x")?; + return Ok(()); + +} + + +/* + * Tests for times + */ +#[test] +fn no_times_binomial() -> Result<()> { + let expr = "x ⁢ y"; + test("pt", "SimpleSpeak", expr, "x y")?; + return Ok(()); + +} + +#[test] +fn times_following_paren() -> Result<()> { + let expr = " + 2 + ( 3 ) + "; + test("pt", "SimpleSpeak", expr, "2 vezes 3")?; + return Ok(()); + +} + +#[test] +fn times_preceding_paren() -> Result<()> { + let expr = " + ( 2 ) + 3 + "; + test("pt", "SimpleSpeak", expr, "2 vezes 3")?; + return Ok(()); + +} + +#[test] +fn no_times_sqrt() -> Result<()> { + let expr = " + a + b + = + ab + "; + test("pt", "SimpleSpeak", expr, + "a raiz quadrada de a; a raiz quadrada de b; é igual a, a raiz quadrada de a b fim da raiz")?; + return Ok(()); + +} + +/* + * Tests for parens + */ + #[test] + fn no_parens_number() -> Result<()> { + let expr = " + ( + 25 + ) + x + "; + test("pt", "SimpleSpeak", expr, "25 vezes x")?; + return Ok(()); + + } + + #[test] + fn no_parens_monomial() -> Result<()> { + let expr = " + b + ( + xy + ) + "; + test("pt", "SimpleSpeak", expr, "b x y")?; + return Ok(()); + + } + + #[test] + fn no_parens_negative_number() -> Result<()> { + let expr = " + 2+ + ( + −2 + ) + "; + test("pt", "SimpleSpeak", expr, "2 mais menos 2")?; + return Ok(()); + + } + + + #[test] + fn no_parens_negative_number_with_var() -> Result<()> { + let expr = " + ( + −2x + ) + +1 + "; + test("pt", "SimpleSpeak", expr, "menos 2 x, mais 1")?; + return Ok(()); + + } + + #[test] + fn parens_superscript() -> Result<()> { + let expr = " + + + + ( + 2x + ) + 2 + + + "; + test("pt", "SimpleSpeak", expr, "abre parênteses 2 x fecha parênteses ao quadrado")?; + return Ok(()); + + } + + #[test] + fn no_parens_fraction() -> Result<()> { + let expr = " + 2 + + + + ( + 12 + ) + "; + test("pt", "SimpleSpeak", expr, "2 mais 1 meio")?; + return Ok(()); + + } + + + // Tests for the four types of intervals in SimpleSpeak + #[test] + fn parens_interval_open_open() -> Result<()> { + let expr = " + ( + (c,d) + ) + "; + test("pt", "SimpleSpeak",expr, "o intervalo aberto de c a d")?; + return Ok(()); + +} + +#[test] + fn parens_interval_closed_open() -> Result<()> { + let expr = " + [ + [(]c,d) + ) + "; + test("pt", "SimpleSpeak",expr, "o intervalo fechado aberto de c a d")?; + return Ok(()); + +} + + +#[test] +fn parens_interval_open_closed() -> Result<()> { + let expr = " + ( + (c,d] + ] + "; + test("pt", "SimpleSpeak",expr,"o intervalo aberto fechado de c a d")?; + return Ok(()); + +} + + +#[test] +fn parens_interval_closed_closed() -> Result<()> { + let expr = " + [ + [(]c,d] + ] + "; + test("pt", "SimpleSpeak",expr, "o intervalo fechado de c a d")?; + return Ok(()); + +} + + #[test] + fn parens_interval_neg_infinity_open_open() -> Result<()> { + let expr = " + ( + - ∞,d) + ) + "; + test("pt", "SimpleSpeak",expr, + "o intervalo aberto de menos infinito a d")?; + return Ok(()); + +} + + #[test] + fn parens_interval_neg_infinity_open_closed() -> Result<()> { + let expr = " + ( + - ∞,d] + ] + "; + test("pt", "SimpleSpeak",expr, + "o intervalo aberto fechado de menos infinito a d")?; + return Ok(()); + +} + diff --git a/tests/Languages/pt/alphabets.rs b/tests/Languages/pt/alphabets.rs new file mode 100644 index 000000000..b648e59c4 --- /dev/null +++ b/tests/Languages/pt/alphabets.rs @@ -0,0 +1,438 @@ +/// TESTES DO IDIOMA pt — como reconciliar com as regras +/// Testes de alfabetos — versão pt. +/// * this has tests focused on the various alphabets +use crate::common::*; +use anyhow::Result; + + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn special_alphabet_chars() -> Result<()> { + let expr = " ℌ,ℭ"; + test("pt", "SimpleSpeak", expr, "fraktur cap h comma fraktur cap c")?; + let expr = " ℍ,ℿ"; + test("pt", "SimpleSpeak", expr, "double struck cap h comma double struck cap pi")?; + let expr = " ℐ,ℳ"; + test("pt", "SimpleSpeak", expr, "script cap i comma script cap m")?; + return Ok(()); + +} + +#[test] +fn greek() -> Result<()> { + let expr = " Α,Ω"; + // a segunda vírgula é a pausa automática do motor depois do operador; + // o inglês tem a mesma ("cap alpha comma, cap omega"). + test("pt", "SimpleSpeak", expr, "maiúscula alfa vírgula, maiúscula ômega")?; + let expr = " α,ω"; + test("pt", "SimpleSpeak", expr, "alfa vírgula, ômega")?; + return Ok(()); +} + +#[test] +#[ignore] // caracteres de área privada do MathType: dependem do unicode-full.yaml, removido de pt de propósito +fn greek_mathtype_private() -> Result<()> { + let expr = " \u{f8d3},\u{f8e0}"; + test("pt", "SimpleSpeak", expr, "maiúscula delta vazado, vírgula maiúscula úpsilon vazado")?; + return Ok(()); +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn cap_cyrillic() -> Result<()> { + let expr = " А,Я"; + test("pt", "SimpleSpeak", expr, "cap a comma cap ya")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn parenthesized() -> Result<()> { + let expr = " ⒜,⒵"; + test("pt", "SimpleSpeak", expr, "parenthesized eigh comma parenthesized z")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn circled() -> Result<()> { + let expr = " Ⓐ,Ⓩ"; + test("pt", "SimpleSpeak", expr, "circled cap eigh comma circled cap z")?; + let expr = " ⓐ,ⓩ"; + test("pt", "SimpleSpeak", expr, "circled eigh comma circled z")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn fraktur() -> Result<()> { + let expr = " 𝔄,𝔜"; + test("pt", "SimpleSpeak", expr, "fraktur cap eigh comma fraktur cap y")?; + let expr = " 𝔞,𝔷"; + test("pt", "SimpleSpeak", expr, "fraktur eigh comma fraktur z")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "fraktur cap eigh comma fraktur cap y")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "fraktur eigh comma fraktur z")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn bold_fraktur() -> Result<()> { + let expr = " 𝕬,𝖅"; + test("pt", "SimpleSpeak", expr, "fraktur bold cap eigh, comma fraktur bold cap z")?; + let expr = " 𝖆,𝖟"; + test("pt", "SimpleSpeak", expr, "fraktur bold eigh comma fraktur bold z")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "fraktur bold cap eigh, comma fraktur bold cap z")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "fraktur bold eigh comma fraktur bold z")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn double_struck() -> Result<()> { + let expr = " 𝔸,𝕐"; + test("pt", "SimpleSpeak", expr, "double struck cap eigh, comma double struck cap y")?; + let expr = " 𝕒,𝕫"; + test("pt", "SimpleSpeak", expr, "double struck eigh comma double struck z")?; + let expr = " 𝟘,𝟡"; + test("pt", "SimpleSpeak", expr, "double struck 0 comma double struck 9")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "double struck cap eigh, comma double struck cap y")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "double struck eigh comma double struck z")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "double struck 0 comma double struck 9")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn script() -> Result<()> { + let expr = " 𝒜,𝒵"; + test("pt", "SimpleSpeak", expr, "script cap eigh comma script cap z")?; + let expr = " 𝒶,𝓏"; + test("pt", "SimpleSpeak", expr, "script eigh comma script z")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "script cap eigh comma script cap z")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "script eigh comma script z")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn bold_script() -> Result<()> { + let expr = " 𝓐,𝓩"; + test("pt", "SimpleSpeak", expr, "script bold cap eigh comma script bold cap z")?; + let expr = " 𝓪,𝔃"; + test("pt", "SimpleSpeak", expr, "script bold eigh comma script bold z")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "script bold cap eigh comma script bold cap z")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "script bold eigh comma script bold z")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn bold() -> Result<()> { + let expr = " 𝐀,𝐙"; + test("pt", "SimpleSpeak", expr, "bold cap eigh comma bold cap z")?; + let expr = " 𝐚,𝐳"; + test("pt", "SimpleSpeak", expr, "bold eigh comma bold z")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold cap eigh comma bold cap z")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold eigh comma bold z")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn italic() -> Result<()> { + let expr = " 𝐴,𝑍"; + test("pt", "SimpleSpeak", expr, "cap eigh comma cap z")?; + let expr = " 𝑎,𝑧"; + test("pt", "SimpleSpeak", expr, "eigh comma z")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "cap eigh comma cap z")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "eigh comma z")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn sans_serif() -> Result<()> { + let expr = " 𝖠,𝖹"; + test("pt", "SimpleSpeak", expr, "cap eigh comma cap z")?; + let expr = " 𝖺,𝗓"; + test("pt", "SimpleSpeak", expr, "eigh comma z")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "cap eigh comma cap z")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "eigh comma z")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn sans_serif_bold() -> Result<()> { + let expr = " 𝗔,𝗭"; + test("pt", "SimpleSpeak", expr, "bold cap eigh comma bold cap z")?; + let expr = " 𝗮,𝘇"; + test("pt", "SimpleSpeak", expr, "bold eigh comma bold z")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold cap eigh comma bold cap z")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold eigh comma bold z")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn sans_serif_italic() -> Result<()> { + let expr = " 𝘈,𝘡"; + test("pt", "SimpleSpeak", expr, "cap eigh comma cap z")?; + let expr = " 𝘢,𝘻"; + test("pt", "SimpleSpeak", expr, "eigh comma z")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "cap eigh comma cap z")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "eigh comma z")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn sans_serif_bold_italic() -> Result<()> { + let expr = " 𝘼,𝙕"; + test("pt", "SimpleSpeak", expr, "bold cap eigh comma bold cap z")?; + let expr = " 𝙖,𝙯"; + test("pt", "SimpleSpeak", expr, "bold eigh comma bold z")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold cap eigh comma bold cap z")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold eigh comma bold z")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn monospace() -> Result<()> { + let expr = " 𝙰,𝚉"; + test("pt", "SimpleSpeak", expr, "cap eigh comma cap z")?; + let expr = " 𝚊,𝚣"; + test("pt", "SimpleSpeak", expr, "eigh comma z")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "cap eigh comma cap z")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "eigh comma z")?; + return Ok(()); + +} + + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn bold_greek() -> Result<()> { + let expr = " 𝚨,𝛀"; + test("pt", "SimpleSpeak", expr, "bold cap alpha comma bold cap omega")?; + let expr = " 𝛂,𝛚"; + test("pt", "SimpleSpeak", expr, "bold alpha comma bold omega")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold cap alpha comma bold cap omega")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold alpha comma bold omega")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn bold_greek_others() -> Result<()> { + let expr = " 𝛛,𝛡"; + test("pt", "SimpleSpeak", expr, "bold partial derivative, comma bold pi")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold partial derivative, comma bold pi")?; + return Ok(()); + +} + + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn italic_greek() -> Result<()> { + let expr = " 𝛢,𝛺"; + test("pt", "SimpleSpeak", expr, "maiúscula alfa vírgula maiúscula ômega")?; + let expr = " 𝛼,𝜔"; + test("pt", "SimpleSpeak", expr, "alfa vírgula ômega")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "maiúscula alfa vírgula maiúscula ômega")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "alfa vírgula ômega")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn italic_greek_others() -> Result<()> { + let expr = " 𝜕,𝜛"; + test("pt", "SimpleSpeak", expr, "partial derivative comma pi")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "partial derivative comma pi")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn bold_italic_greek() -> Result<()> { + let expr = " 𝜜,𝜴"; + test("pt", "SimpleSpeak", expr, "bold cap alpha comma bold cap omega")?; + let expr = " 𝜶,𝝎"; + test("pt", "SimpleSpeak", expr, "bold alpha comma bold omega")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold cap alpha comma bold cap omega")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold alpha comma bold omega")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn bold_italic_greek_others() -> Result<()> { + let expr = " 𝝏,𝝕"; + test("pt", "SimpleSpeak", expr, "bold partial derivative, comma bold pi")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold partial derivative, comma bold pi")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn sans_serif_bold_greek() -> Result<()> { + let expr = " 𝝖,𝝮"; + test("pt", "SimpleSpeak", expr, "bold cap alpha comma bold cap omega")?; + let expr = " 𝝰,𝞈"; + test("pt", "SimpleSpeak", expr, "bold alpha comma bold omega")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold cap alpha comma bold cap omega")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold alpha comma bold omega")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn sans_serif_bold_greek_others() -> Result<()> { + let expr = " 𝞉,𝞏"; + test("pt", "SimpleSpeak", expr, "bold partial derivative, comma bold pi")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold partial derivative, comma bold pi")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn sans_serif_bold_italic_greek() -> Result<()> { + let expr = " 𝞐,𝞨"; + test("pt", "SimpleSpeak", expr, "bold cap alpha comma bold cap omega")?; + let expr = " 𝞪,𝟂"; + test("pt", "SimpleSpeak", expr, "bold alpha comma bold omega")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold cap alpha comma bold cap omega")?; + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold alpha comma bold omega")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito (cai no inglês) +fn sans_serif_bold_italic_greek_others() -> Result<()> { + let expr = " 𝟃,𝟉"; + test("pt", "SimpleSpeak", expr, "bold partial derivative, comma bold pi")?; + // MathType private space versions + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "bold partial derivative, comma bold pi")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito +fn pua_regular() -> Result<()> { + let expr = " ,"; + test("pt", "SimpleSpeak", expr, "cap eigh comma cap z")?; + return Ok(()); + +} + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito +fn turned() -> Result<()> { + let expr = " Ⅎ,⅄"; + test("pt", "SimpleSpeak", expr, "turned cap f comma turned sans-serif cap y")?; + return Ok(()); + + } + +#[test] +#[ignore] // depende de unicode-full.yaml, removido de pt de propósito +fn enclosed_numbers() -> Result<()> { + let expr = " ①,⑨"; + test("pt", "SimpleSpeak", expr, "circled 1 comma circled 9")?; + let expr = " ⑴,⑼"; + test("pt", "SimpleSpeak", expr, "parenthesized 1 comma parenthesized 9")?; + let expr = " ⒈,⒐"; + test("pt", "SimpleSpeak", expr, "1 with period comma 9 with period")?; + let expr = " ⓵,⓽"; + test("pt", "SimpleSpeak", expr, "double circled 1 comma double circled 9")?; + return Ok(()); + +} diff --git a/tests/Languages/pt/chemistry.rs b/tests/Languages/pt/chemistry.rs new file mode 100644 index 000000000..b8448add8 --- /dev/null +++ b/tests/Languages/pt/chemistry.rs @@ -0,0 +1,721 @@ +/// TESTES DO IDIOMA pt — como reconciliar com as regras +/// Tests for rules shared between various speech styles: +/// * modified var +use crate::common::*; +use anyhow::Result; + +#[test] +fn salt() -> Result<()> { + let expr = "NaCl"; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Terse")], expr, "maiúscula n a; maiúscula c l")?; + return Ok(()); + +} + +#[test] +fn water() -> Result<()> { + let expr = "H2O"; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test_prefs("pt", "ClearSpeak", vec![("Verbosity", "Terse")], expr, "maiúscula h, 2, maiúscula o")?; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test_prefs("pt", "ClearSpeak", vec![("Verbosity", "Medium")], expr, "maiúscula h, sub 2, maiúscula o")?; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test_prefs("pt", "ClearSpeak", vec![("Verbosity", "Verbose")], expr, "maiúscula h, subscrito 2, maiúscula o")?; + return Ok(()); + +} + +#[test] +fn carbon() -> Result<()> { + let expr = "C"; // not enough to trigger recognition + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Terse")], expr, "maiúscula c")?; + return Ok(()); + +} + +#[test] +fn sulfate() -> Result<()> { + let expr = " + [SO4] + 2− + "; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test_prefs("pt", "ClearSpeak", vec![("Verbosity", "Medium")], expr, "abre colchetes, maiúscula s, maiúscula o, sub 4; fecha colchetes super 2 menos")?; + return Ok(()); + +} + +#[test] +fn aluminum_sulfate() -> Result<()> { + let expr = "Al2 + (SO4)3"; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test_prefs("pt", "ClearSpeak", vec![("Verbosity", "Terse")], expr, "maiúscula a l, 2; abre parênteses, maiúscula s, maiúscula o, 4; fecha parênteses 3")?; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test_prefs("pt", "ClearSpeak", vec![("Verbosity", "Medium")], expr, "maiúscula a l, sub 2; abre parênteses, maiúscula s, maiúscula o, sub 4; fecha parênteses sub 3")?; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test_prefs("pt", "ClearSpeak", vec![("Verbosity", "Verbose")], expr, "maiúscula a l, subscrito 2; abre parênteses, maiúscula s, maiúscula o, subscrito 4; fecha parênteses subscrito 3")?; + return Ok(()); + +} + +#[test] +fn ethanol_bonds() -> Result<()> { + let expr = " + + C + H 3 + − + C + H 2 + − + O + H + + "; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test_prefs("pt", "ClearSpeak", vec![("Verbosity", "Terse")], expr, "maiúscula c, maiúscula h, 3, ligação simples, maiúscula c, maiúscula h, 2, ligação simples, maiúscula o, maiúscula h")?; + + return Ok(()); + +} + +#[test] +fn dichlorine_hexoxide() -> Result<()> { + let expr = " + + [ClO2] + + + + + [ClO4] + - + + "; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Terse")], + expr, "abre colchetes, maiúscula c l; maiúscula o, 2; fecha colchetes mais; \ + abre colchetes, maiúscula c l; maiúscula o, 4; fecha colchetes menos")?; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Medium")], + expr, "abre colchetes, maiúscula c l; maiúscula o, sub 2; fecha colchetes super mais; \ + abre colchetes, maiúscula c l; maiúscula o, sub 4; fecha colchetes super menos")?; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Verbose")], + expr, "abre colchetes, maiúscula c l; maiúscula o, subscrito 2; fecha colchetes sobrescrito mais; \ + abre colchetes, maiúscula c l; maiúscula o, subscrito 4; fecha colchetes sobrescrito menos")?; + return Ok(()); + +} + + +#[test] +fn ethylene_with_bond() -> Result<()> { + let expr = " + H2C + = + CH2 + "; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Terse")], expr, "maiúscula h, 2, maiúscula c; ligação dupla, maiúscula c, maiúscula h, 2")?; + return Ok(()); + +} + +#[test] +fn ferric_chloride_aq() -> Result<()> { + let expr = " + Fe + Cl3 + (aq) + "; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Terse")], expr, "maiúscula f e; maiúscula c l, 3, aquoso")?; + return Ok(()); + + } + +#[test] +fn ethylene_with_colon_bond() -> Result<()> { + let expr = " + H2C + :: + CH2 + "; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Terse")], expr, "maiúscula h, 2, maiúscula c; ligação dupla, maiúscula c, maiúscula h, 2")?; + return Ok(()); + +} + +#[test] +fn beta_decay() -> Result<()> { + let expr = " + + C + + 6 + 14 + + → + + N + + 7 + 14 + + + + + e + + + − + 1 + + 0 + + "; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test_prefs("pt", "ClearSpeak", vec![("Verbosity", "Terse")], expr, + "14, 6, maiúscula c; forma, 14, 7, maiúscula n; mais 0, menos 1, e")?; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test_prefs("pt", "ClearSpeak", vec![("Verbosity", "Medium")], expr, + "super 14, sub 6, maiúscula c; reage formando; super 14, sub 7, maiúscula n; mais super 0, sub menos 1, e")?; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test_prefs("pt", "ClearSpeak", vec![("Verbosity", "Verbose")], expr, + "sobrescrito 14, subscrito 6, maiúscula c; reage formando; sobrescrito 14, subscrito 7, maiúscula n; mais, sobrescrito 0, subscrito menos 1, e")?; + return Ok(()); + +} + +#[test] +fn mhchem_beta_decay() -> Result<()> { + let expr = " + + + + + + + A + + + + + + + + + 6 + + + + + + + + + 14 + + + + + + + + + + + + A + + + + + + + + + 2 + + + + + + + + 6 + + + + + + + + + + + + 2 + + + + + + + + 14 + + + + + + C + + + + ⟶ + + + + + + + + A + + + + + + + + + 7 + + + + + + + + + 14 + + + + + + + + + + + + A + + + + + + + + + 2 + + + + + + + + 7 + + + + + + + + + + + + 2 + + + + + + + + 14 + + + + + + N + + + + + + + + + + + A + + + + + + + + + − + 1 + + + + + + + + + 0 + + + + + + + + + + + + A + + + + + + + + + 2 + + + + + + + + − + 1 + + + + + + + + + + + + 2 + + + + + + + + 0 + + + + + + e + + + "; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test_prefs("pt", "ClearSpeak", vec![("Verbosity", "Terse")], expr, + "14, 6, maiúscula c; forma, 14, 7, maiúscula n; mais 0, menos 1, e")?; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test_prefs("pt", "ClearSpeak", vec![("Verbosity", "Medium")], expr, + "super 14, sub 6, maiúscula c; reage formando; super 14, sub 7, maiúscula n; mais super 0, sub menos 1, e")?; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test_prefs("pt", "ClearSpeak", vec![("Verbosity", "Verbose")], expr, + "sobrescrito 14, subscrito 6, maiúscula c; reage formando; sobrescrito 14, subscrito 7, maiúscula n; mais, sobrescrito 0, subscrito menos 1, e")?; + return Ok(()); + +} + +#[test] +fn hcl_na_produz() -> Result<()> { + let expr = " + 2HCl+2Na + → + 2NaCl+ + H 2 + + "; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Verbose")], expr, + "2, maiúscula h, maiúscula c l; mais 2 maiúscula n a; reage formando; 2, maiúscula n a; maiúscula c l; mais maiúscula h, subscrito 2")?; + return Ok(()); + +} + +#[test] +fn mhchem_so4_2mais() -> Result<()> { + let expr = " + + + SO + + + + + + + A + + + + + + + + 4 + + + + + + + + + + A + + + + + + 2 + + + + + + "; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Terse")], expr, "maiúscula s; maiúscula o, 4, 2 mais")?; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Medium")], expr, "maiúscula s; maiúscula o, sub 4, super 2 mais")?; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Verbose")], expr, "maiúscula s; maiúscula o, subscrito 4, sobrescrito 2 mais")?; + return Ok(()); + +} + + +#[test] +fn mhchem_hcl_aq_etc() -> Result<()> { + let expr = " + + 2 + + + + + HCl + + + ( + + aq + + ) + + + + + 2 + + + + + Na + + + ( + + s + + ) + + + ⟶ + + + 2 + + + + + NaCl + + + ( + + aq + + ) + + + + + + H + + + + + + + A + + + + + + + + 2 + + + + + + ( + + g + + ) + + "; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Terse")], + expr, "2, maiúscula h, maiúscula c l; aquoso; mais, 2, maiúscula n a; sólido; forma; 2, maiúscula n a; maiúscula c l; aquoso; mais, maiúscula h, 2; gasoso")?; + + return Ok(()); + +} + + +#[test] +fn mhchem_barbed_equilibrium() -> Result<()> { + let expr = " + + + + H + 2 + + + ⁣ + + ( + g + ) + + + + + + + + I + 2 + + + ⁣ + + ( + g + ) + + + ⁢ + + + ↽ + - + + + - + ⇀ + + + ⁢ + 2 + ⁢ + + H + ⁣ + I + ⁣ + + ( + g + ) + + + + + "; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Terse")], + expr, "maiúscula h, 2; gasoso; mais; maiúscula i, 2; gasoso; está em equilíbrio com, 2, maiúscula h, maiúscula i, gasoso")?; + return Ok(()); + +} + + + +#[test] +fn mhchem_roman_in_sobrescrito() -> Result<()> { + let expr = " + + + Fe + + II + + ⁣ + + Fe + + III + + ⁣ + + O + 4 + + + + "; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Terse")], + expr, "maiúscula f e, 2; maiúscula f e, 3; maiúscula o, 4")?; + return Ok(()); + +} + + diff --git a/tests/Languages/pt/escuta.rs b/tests/Languages/pt/escuta.rs new file mode 100644 index 000000000..812be28be --- /dev/null +++ b/tests/Languages/pt/escuta.rs @@ -0,0 +1,33 @@ +use crate::common::*; + +fn falar(estilo: &str, verbosidade: &str, mathml: &str) -> String { + set_rules_dir(abs_rules_dir_path()).unwrap(); + set_preference("Language", "pt".to_string()).unwrap(); + set_preference("SpeechStyle", estilo.to_string()).unwrap(); + set_preference("Verbosity", verbosidade.to_string()).unwrap(); + set_preference("SpeechOverrides_CapitalLetters", "".to_string()).unwrap(); + set_preference("ClearSpeak_SetMemberSymbol", "Auto".to_string()).unwrap(); + match set_mathml(mathml.to_string()) { + Ok(_) => match get_spoken_text() { + Ok(fala) => regex::Regex::new(r" +").unwrap().replace_all(&fala, " ").to_string(), + Err(e) => format!("[ERRO ao falar: {}]", errors_to_string(&e)), + }, + Err(e) => format!("[ERRO no MathML: {}]", errors_to_string(&e)), + } +} + +#[test] +#[ignore = "gerador do pacote de escuta; rode com --ignored --nocapture"] +fn gerar_pacote_escuta() { + let tsv = include_str!("escuta_expressoes.tsv"); + for linha in tsv.lines().filter(|l| !l.starts_with('#') && !l.trim().is_empty()) { + let campos: Vec<&str> = linha.split('\t').collect(); + assert_eq!(campos.len(), 4, "linha malformada no tsv: {linha}"); + let (area, titulo, mathml) = (campos[0], campos[1], campos[3]); + for estilo in ["ClearSpeak", "SimpleSpeak"] { + for verb in ["Terse", "Medium", "Verbose"] { + println!("FALA\t{area}\t{titulo}\t{estilo}\t{verb}\t{}", falar(estilo, verb, mathml)); + } + } + } +} diff --git a/tests/Languages/pt/escuta_expressoes.tsv b/tests/Languages/pt/escuta_expressoes.tsv new file mode 100644 index 000000000..ce729ff9c --- /dev/null +++ b/tests/Languages/pt/escuta_expressoes.tsv @@ -0,0 +1,30 @@ +aritmetica Fração mista e decimal 3\frac{1}{2} + 0{,}75 = 4{,}25 312+0,75=4,25 +aritmetica Bhaskara x = \frac{-b \pm \sqrt{b^{2} - 4ac}}{2a} x=−b±b2−4ac2a +aritmetica Potência e raiz \sqrt[3]{27} = 3^{1} \quad \text{e} \quad 2^{10} = 1024 273=31e210=1024 +aritmetica Porcentagem 15\% \cdot 200 = 30 15%·200=30 +aritmetica Fração de fração \frac{\frac{1}{2}}{\frac{3}{4}} = \frac{2}{3} 1234=23 +conjuntos_logica Pertinência e compreensão \{ x \in \mathbb{Z} : x > 5 \} {x∈ℤ:x>5} +conjuntos_logica União e interseção (A \cup B) \cap C = (A \cap C) \cup (B \cap C) (A∪B)∩C=(A∩C)∪(B∩C) +conjuntos_logica Implicação p \Rightarrow q \equiv \neg p \lor q p⇒q≡¬p∨q +conjuntos_logica Quantificadores \forall \varepsilon > 0 \; \exists \delta > 0 ∀ε>0∃δ>0 +conjuntos_logica Continência \emptyset \subseteq A \subseteq \mathbb{N} ∅⊆A⊆ℕ +calculo Limite fundamental \lim_{x \to 0} \frac{\sin x}{x} = 1 limx→0sinxx=1 +calculo Derivada \frac{d}{dx}\left(x^{2} e^{x}\right) = 2x e^{x} + x^{2} e^{x} ddx(x2ex)=2xex+x2ex +calculo Integral definida \int_{0}^{1} x^{2}\,dx = \frac{1}{3} ∫01x2dx=13 +calculo Série \sum_{n=1}^{\infty} \frac{1}{n^{2}} = \frac{\pi^{2}}{6} ∑n=1∞1n2=π26 +calculo Derivada parcial \frac{\partial f}{\partial x} = 2xy ∂f∂x=2xy +algebra_linear Matriz e determinante A = \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix}, \quad \det(A) = -2 A=(1234),det(A)=−2 +algebra_linear Sistema A\mathbf{x} = \mathbf{b}, \quad \mathbf{x} \in \mathbb{R}^{n} A𝐱=𝐛,𝐱∈ℝn +algebra_linear Autovalor A v = \lambda v Av=λv +algebra_linear Produto interno e norma \langle u, v \rangle = \|u\| \|v\| \cos\theta ⟨u,v⟩=‖u‖‖v‖cosθ +algebra_linear Transposta e inversa (AB)^{T} = B^{T} A^{T}, \quad A^{-1}A = I (AB)T=BTAT,A−1A=I +quimica Água \mathrm{2H_2 + O_2 \longrightarrow 2H_2O} 2H2+O2⟶2H2O +quimica Ácido carbônico \mathrm{CO_2 + H_2O \rightleftharpoons H_2CO_3} CO2+H2O⇌H2CO3 +quimica Íon \mathrm{Na^{+} + Cl^{-} \longrightarrow NaCl} Na++Cl−⟶NaCl +quimica Concentração [\mathrm{H^{+}}] = 10^{-7}\ \mathrm{mol/L} [H+]=10−7 mol/L +quimica Isótopo {}^{14}_{6}\mathrm{C} 614C +estatistica Média amostral \bar{x} = \frac{1}{n} \sum_{i=1}^{n} x_{i} x¯=1n∑i=1nxi +estatistica Variância \sigma^{2} = \frac{\sum (x_{i} - \mu)^{2}}{N} σ2=∑(xi−μ)2N +estatistica Probabilidade condicional P(A \mid B) = \frac{P(A \cap B)}{P(B)} P(A∣B)=P(A∩B)P(B) +estatistica Binomial \binom{n}{k} p^{k} (1-p)^{n-k} (nk)pk(1−p)n−k +estatistica Normal X \sim N(\mu, \sigma^{2}) X~N(μ,σ2) diff --git a/tests/Languages/pt/intent.rs b/tests/Languages/pt/intent.rs new file mode 100644 index 000000000..df164bc1b --- /dev/null +++ b/tests/Languages/pt/intent.rs @@ -0,0 +1,72 @@ +/// TESTES DO IDIOMA pt — como reconciliar com as regras +/// +/// As strings esperadas abaixo foram escritas para refletir a intenção +/// das regras em Rules/Languages/pt. Vírgulas e ponto-e-vírgula marcam +/// PAUSAS inseridas pelo motor e podem divergir levemente na primeira +/// execução. Método do guia oficial de tradutores: +/// 1. rode `cargo test Languages::pt` +/// 2. para cada falha, compare `left` (esperado) com `right` (gerado) +/// 3. se o GERADO estiver correto em português, copie-o para o teste; +/// se estiver errado, conserte a REGRA, nunca o teste. +/// + +/// Tests for rules shared between various speech styles: +/// * this has tests focused on the various alphabets +use crate::common::*; +use anyhow::Result; + + +#[test] +fn silent_intent_mi() -> Result<()> { + let expr = " 2 x"; + test("pt", "SimpleSpeak", expr, "2")?; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test("pt", "ClearSpeak", expr, "2")?; + return Ok(()); + +} + +#[test] +fn silent_intent_msup() -> Result<()> { + let expr = " + + H + 2 + "; + test("pt", "SimpleSpeak", expr, "maiúscula h 2")?; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test("pt", "ClearSpeak", expr, "maiúscula h 2")?; + return Ok(()); + +} + +#[test] +fn silent_intent_underscore() -> Result<()> { + let expr = " + + H + 2 + "; + test("pt", "SimpleSpeak", expr, "maiúscula h 2")?; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test("pt", "ClearSpeak", expr, "maiúscula h 2")?; + return Ok(()); + +} + +#[test] +fn intent_prob_x() -> Result<()> { + let expr = " + + x + P + "; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test("pt", "ClearSpeak", expr, "probabilidade de x")?; + return Ok(()); + +} diff --git a/tests/Languages/pt/shared.rs b/tests/Languages/pt/shared.rs new file mode 100644 index 000000000..cf860236b --- /dev/null +++ b/tests/Languages/pt/shared.rs @@ -0,0 +1,346 @@ +/// TESTES DO IDIOMA pt — como reconciliar com as regras +/// +/// As strings esperadas abaixo foram escritas para refletir a intenção +/// das regras em Rules/Languages/pt. Vírgulas e ponto-e-vírgula marcam +/// PAUSAS inseridas pelo motor e podem divergir levemente na primeira +/// execução. Método do guia oficial de tradutores: +/// 1. rode `cargo test Languages::pt` +/// 2. para cada falha, compare `left` (esperado) com `right` (gerado) +/// 3. se o GERADO estiver correto em português, copie-o para o teste; +/// se estiver errado, conserte a REGRA, nunca o teste. +/// +/// ATENÇÃO ao histórico: o arquivo espanhol equivalente continha testes +/// que não refletiam as regras (ex.: esperava "logaritmo natural" com a +/// regra dizendo "natural log") e dano de busca-e-troca (ex.: "eigh"→"8"). +/// Não herde strings esperadas de es/ sem conferir. + +/// Tests for rules shared between various speech styles: +/// * modified var +use crate::common::*; +use anyhow::Result; + +#[test] +fn modified_vars() -> Result<()> { + let expr = " + a ` + b ~ + c ̆ + b ̌ + c ` + + x . + y ˙ + z ¨ + u ⃛ + v ⃜ + + x ^ + + t → + "; + test("pt", "SimpleSpeak", expr, + "a acento grave, b til, c breve, b cáron, c acento grave; mais; x ponto, y ponto, z dois pontos, u três pontos acima; v quatro pontos acima; mais x circunflexo, mais vetor t")?; + return Ok(()); + +} + +#[test] +fn limit() -> Result<()> { + let expr = " + + lim + x → 0 + + + + sin ⁡ x + x + + + "; + test("pt", "SimpleSpeak", expr, "limite quando x tende a 0, de, fração, seno de x, sobre x, fim da fração")?; + return Ok(()); + +} + +#[test] +fn limit_from_below() -> Result<()> { + let expr = " + + lim + x ↗ 0 + + + sin ⁡ x + + "; + test("pt", "SimpleSpeak", expr, "limite quando x tende por baixo a 0, de seno de x")?; + return Ok(()); + +} + + +#[test] +fn binomial_mmultiscripts() -> Result<()> { + let expr = "Cmn"; + test("pt", "SimpleSpeak", expr, "n escolhe m")?; + return Ok(()); + +} + + +#[test] +fn permutation_mmultiscripts() -> Result<()> { + let expr = "Pkn"; + test("pt", "SimpleSpeak", expr, "k permutações de n")?; + return Ok(()); + +} + +#[test] +fn permutation_mmultiscripts_sup() -> Result<()> { + let expr = "Pkn"; + test("pt", "SimpleSpeak", expr, "k permutações de n")?; + return Ok(()); + +} + +#[test] +fn permutation_msubsup() -> Result<()> { + let expr = "Pkn"; + test("pt", "SimpleSpeak", expr, "k permutações de n")?; + return Ok(()); + +} + +#[test] +fn tensor_mmultiscripts() -> Result<()> { + let expr = " + R i j k l + "; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Verbose")], expr, + "maiúscula r com 4 pós-escritos, subscrito i sobrescrito j subscrito k subscrito l")?; + // em Medium as regras usam a forma abreviada ('sub'/'super'), como no inglês; + // a forma longa fica reservada ao Verbose (SharedRules/default.yaml). + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Medium")], expr, + "maiúscula r com 4 pós-escritos, sub i super j sub k sub l")?; + return Ok(()); + +} + +#[test] +fn huge_num_mmultiscripts() -> Result<()> { + let expr = " + R i j k l m + I J K L + "; + test_prefs("pt", "SimpleSpeak", vec![("Verbosity", "Verbose")], expr, + "maiúscula r com 4 pré-escritos, pré-subscrito maiúscula i, pré-sobrescrito maiúscula j e pré-escritos alternados maiúscula k nenhum maiúscula l nenhum fim dos pré-escritos e com 5 pós-escritos, subscrito i sobrescrito j subscrito k subscrito l e índices alternados m nenhum fim dos índices")?; + return Ok(()); + +} + +#[test] +fn prime() -> Result<()> { + let expr = " x′ "; + test("pt", "SimpleSpeak", expr, "x linha")?; + return Ok(()); + +} + +#[test] +fn given() -> Result<()> { + let expr = "P(A|B)"; + // "dado": em P(A|B) a barra é probabilidade condicional nos dois estilos, + // como no inglês ("cap eigh given cap b"). + test("pt", "SimpleSpeak", expr, "maiúscula p; abre parênteses, maiúscula a dado maiúscula b; fecha parênteses")?; + test("pt", "ClearSpeak", expr, "maiúscula p; abre parênteses, maiúscula a dado maiúscula b; fecha parênteses")?; // not good, but follows the spec + return Ok(()); + +} + +#[test] +fn simple_msubsup() -> Result<()> { + let expr = " + + + x + + k + + + i + + + + "; + test("pt", "SimpleSpeak", expr, "x subscrito k elevado a i")?; + return Ok(()); + +} + +#[test] +fn presentation_mathml_in_semantics() -> Result<()> { + let expr = " + + {\\displaystyle x_k^i} + + + x + + k + + + i + + + + + "; + test("pt", "SimpleSpeak", expr, "x subscrito k elevado a i")?; + return Ok(()); + +} + +#[test] +fn ignore_period() -> Result<()> { + // from https://en.wikipedia.org/wiki/Probability + let expr = " + + {\\displaystyle x_k^i} + + + + P + ( + A + + +  e  + + + B + ) + = + P + ( + A + ∩ + B + ) + = + P + ( + A + ) + P + ( + B + ) + . + + + + + "; + test("pt", "SimpleSpeak", expr, "maiúscula p; abre parênteses, maiúscula a e maiúscula b; fecha parênteses; é igual a; maiúscula p; abre parênteses, maiúscula a interseção maiúscula b; fecha parênteses; é igual a; maiúscula p de maiúscula a; maiúscula p de maiúscula b")?; + return Ok(()); + +} + +#[test] +fn ignore_mtext_period() -> Result<()> { + let expr = "{2}."; + test("pt", "SimpleSpeak", expr, "o conjunto 2")?; + return Ok(()); + +} + +#[test] +fn ignore_comma() -> Result<()> { + // from https://en.wikipedia.org/wiki/Probability + let expr = " + + + ϕ + ( + x + ) + = + c + + e + + − + + h + + 2 + + + + x + + 2 + + + + + , + + +"; + test("pt", "SimpleSpeak", expr, "fi reto de x, é igual a; c vezes, e elevado à potência menos h ao quadrado, x ao quadrado")?; + return Ok(()); + +} + +#[test] +#[ignore] // issue #14 +fn ignore_period_and_space() -> Result<()> { + // from https://en.wikipedia.org/wiki/Probability + let expr = " + + + P + ( + A + ∣ + B + ) + = + + + + P + ( + A + ∩ + B + ) + + + P + ( + B + ) + + + + . + + + +"; + // ClearSpeak reativado: Rules/Languages/pt/ClearSpeak_Rules.yaml traduzido. + // Pausas (vírgula/ponto-e-vírgula) podem precisar de reconciliação via cargo test. + test("pt", "ClearSpeak", expr, "fi reto de x é igual a; c, e elevado a menos h ao quadrado x ao quadrado, fim do expoente")?; + return Ok(()); + +} + + +#[test] +fn mn_with_space() -> Result<()> { + let expr = "1 234 567"; + test("pt", "SimpleSpeak", expr, "1234567")?; + return Ok(()); + +} diff --git a/tests/Languages/pt/unicode_full.rs b/tests/Languages/pt/unicode_full.rs new file mode 100644 index 000000000..1d1d659d7 --- /dev/null +++ b/tests/Languages/pt/unicode_full.rs @@ -0,0 +1,159 @@ +/// TESTES DO IDIOMA pt — decisões de terminologia registradas em +/// Rules/Languages/pt/ACHADOS.md, seção 8. +/// +/// Este arquivo cobre duas decisões que antes não tinham teste nenhum: +/// * 8.2 — numerais romanos Unicode (0x2160-0x217F) falam o VALOR; +/// * 8.3 — os expoentes ⁰ e ⁱ usam a forma cardinal. +/// e fixa o comportamento ASCII que foi decidido PRESERVAR, para que uma +/// mudança futura no caminho de inferência de contexto não passe em silêncio. +/// +/// Todas as expectativas abaixo foram capturadas da saída real do motor, sob +/// as preferências do próprio harness — não foram escritas a partir do que se +/// esperava que saísse. +use crate::common::*; +use anyhow::Result; + +// --------------------------------------------------------------------------- +// 8.2 — Numerais romanos Unicode: falar o valor, não a grafia. +// Antes desta decisão a saída era a soletração ("I V", "X I I", "M"). +// --------------------------------------------------------------------------- + +#[test] +fn numerais_romanos_unicode_falam_o_valor() -> Result<()> { + // Representativos da faixa 0x2160-0x216F (maiúsculas). + let expr = "Ⅰ"; // 0x2160 + test("pt", "SimpleSpeak", expr, "um")?; + let expr = "Ⅳ"; // 0x2163 — o caso que motivou a decisão + test("pt", "SimpleSpeak", expr, "quatro")?; + let expr = "Ⅻ"; // 0x216b + test("pt", "SimpleSpeak", expr, "doze")?; + let expr = "Ⅼ"; // 0x216c + test("pt", "SimpleSpeak", expr, "cinquenta")?; + let expr = "Ⅽ"; // 0x216d + test("pt", "SimpleSpeak", expr, "cem")?; + let expr = "Ⅾ"; // 0x216e + test("pt", "SimpleSpeak", expr, "quinhentos")?; + let expr = "Ⅿ"; // 0x216f + test("pt", "SimpleSpeak", expr, "mil")?; + return Ok(()); +} + +#[test] +fn numerais_romanos_unicode_minusculos() -> Result<()> { + // A metade minúscula da faixa (0x2170-0x217F) tem os mesmos valores. + let expr = "ⅳ"; // 0x2173 + test("pt", "SimpleSpeak", expr, "quatro")?; + let expr = "ⅿ"; // 0x217f + test("pt", "SimpleSpeak", expr, "mil")?; + return Ok(()); +} + +#[test] +fn numerais_romanos_unicode_em_expressao() -> Result<()> { + // O valor também é falado quando o numeral aparece dentro de uma conta. + let expr = "Ⅳ+Ⅻ"; + test("pt", "SimpleSpeak", expr, "quatro mais doze")?; + // A decisão não depende do estilo de fala. + test("pt", "ClearSpeak", expr, "quatro mais doze")?; + return Ok(()); +} + +#[test] +fn numerais_romanos_unicode_em_mn() -> Result<()> { + // O caractere dedicado vale como numeral tanto em quanto em . + let expr = "Ⅳ"; + test("pt", "SimpleSpeak", expr, "quatro")?; + return Ok(()); +} + +// --------------------------------------------------------------------------- +// 8.3 — Expoentes ⁰ e ⁱ na forma cardinal. +// Antes: "elevado à potência zero" / "elevado à potência i". +// --------------------------------------------------------------------------- + +#[test] +fn expoente_zero_e_i_forma_cardinal() -> Result<()> { + let expr = "x⁰"; // 0x2070 + test("pt", "SimpleSpeak", expr, "x elevado a zero")?; + let expr = "xⁱ"; // 0x2071 + test("pt", "SimpleSpeak", expr, "x elevado a i")?; + // A decisão não depende do estilo de fala. + let expr = "x⁰"; + test("pt", "ClearSpeak", expr, "x elevado a zero")?; + return Ok(()); +} + +#[test] +fn expoente_zero_e_i_isolados() -> Result<()> { + let expr = "⁰"; + test("pt", "SimpleSpeak", expr, "elevado a zero")?; + let expr = "ⁱ"; + test("pt", "SimpleSpeak", expr, "elevado a i")?; + return Ok(()); +} + +#[test] +fn expoentes_ordinais_vizinhos_intactos() -> Result<()> { + // As entradas vizinhas continuam na forma ORDINAL: a decisão 8.3 valeu só + // para ⁰ e ⁱ, que não têm ordinal. Este teste existe para impedir que + // alguém "uniformize" o bloco inteiro por engano (ver ACHADOS 5.2). + let expr = "x⁴"; // 0x2074 + test("pt", "SimpleSpeak", expr, "x elevado à quarta potência")?; + let expr = "xⁿ"; // 0x207f + test("pt", "SimpleSpeak", expr, "x elevado à enésima potência")?; + return Ok(()); +} + +// --------------------------------------------------------------------------- +// GUARDA DE REGRESSÃO — o caminho ASCII foi decidido intocado (ACHADOS 8.2). +// +// Numerais romanos escritos com letras ASCII NÃO passam pelo unicode-full.yaml: +// são marcados por src/canonicalize.rs com `data-roman-numeral` e falados pelas +// regras `default` de / em SharedRules/default.yaml, que soletram. +// Esse é o mesmo ramo de regra que trata I e V como identificador ou variável. +// +// Se algum dia o valor passar a ser falado também no caminho ASCII, ESTES +// testes vão falhar — e é essa a intenção: a mudança tem que ser deliberada. +// --------------------------------------------------------------------------- + +#[test] +fn romanos_ascii_continuam_soletrados_nao_viram_valor() -> Result<()> { + // XIV NÃO deve virar "quatorze". + let expr = "XIV"; + test("pt", "SimpleSpeak", expr, "maiúscula x maiúscula i maiúscula v")?; + let expr = "XIV"; + test("pt", "SimpleSpeak", expr, "maiúscula x maiúscula i maiúscula v")?; + // Também não depende do estilo de fala. + let expr = "XIV"; + test("pt", "ClearSpeak", expr, "maiúscula x maiúscula i maiúscula v")?; + // MIX (1009 em romano) é o caso clássico de colisão com palavra. + let expr = "MIX"; + test("pt", "SimpleSpeak", expr, "maiúscula m maiúscula i maiúscula x")?; + // Minúsculas idem: "xiv" não vira "quatorze". + let expr = "xiv"; + test("pt", "SimpleSpeak", expr, "x i v")?; + return Ok(()); +} + +#[test] +fn letras_ascii_isoladas_continuam_identificadores() -> Result<()> { + // I e V sozinhos são identificadores/variáveis, não os romanos 1 e 5. + // Este é o caminho que a decisão 8.2 mandou explicitamente NÃO tocar. + let expr = "I"; + test("pt", "SimpleSpeak", expr, "maiúscula i")?; + let expr = "V"; + test("pt", "SimpleSpeak", expr, "maiúscula v")?; + // Separados, continuam três identificadores — não o numeral 14. + let expr = "XIV"; + test("pt", "SimpleSpeak", expr, "maiúscula x maiúscula i maiúscula v")?; + return Ok(()); +} + +#[test] +fn romano_ascii_em_estado_de_oxidacao_intacto() -> Result<()> { + // O caminho da química (estado de oxidação) usa romanos ASCII e tem o seu + // próprio tratamento em src/chemistry.rs. A decisão 8.2 não o alcança. + let expr = "IV"; + test("pt", "SimpleSpeak", expr, "maiúscula i maiúscula v")?; + return Ok(()); +} diff --git a/tests/Languages/pt/units.rs b/tests/Languages/pt/units.rs new file mode 100644 index 000000000..47bd73056 --- /dev/null +++ b/tests/Languages/pt/units.rs @@ -0,0 +1,411 @@ +/// TESTES DO IDIOMA pt — como reconciliar com as regras +/// +/// As strings esperadas abaixo foram escritas para refletir a intenção +/// das regras em Rules/Languages/pt. Vírgulas e ponto-e-vírgula marcam +/// PAUSAS inseridas pelo motor e podem divergir levemente na primeira +/// execução. Método do guia oficial de tradutores: +/// 1. rode `cargo test Languages::pt` +/// 2. para cada falha, compare `left` (esperado) com `right` (gerado) +/// 3. se o GERADO estiver correto em português, copie-o para o teste; +/// se estiver errado, conserte a REGRA, nunca o teste. +/// +/// ATENÇÃO ao histórico: o arquivo espanhol equivalente continha testes +/// que não refletiam as regras (ex.: esperava "logaritmo natural" com a +/// regra dizendo "natural log") e dano de busca-e-troca (ex.: "eigh"→"8"). +/// Não herde strings esperadas de es/ sem conferir. + +/// Tests for rules shared between various speech styles: +/// * modified var +use crate::common::*; +use anyhow::Result; + +// The basic layout of the tests is: +// 1. Sweep through all the SI prefixes +// 2. Sweep through each group of SI units +// a) with both singular and plural without prefixes +// b) with both singular and plural with one prefix +// 3. Sweep through each group of units that don't take SI prefixes +// These are broken into chunks so it is easier to see errors, when there are errors + +#[test] +fn prefix_sweep() -> Result<()> { + let expr = r#" + Qg, + Rg, + Yg, + Zg, + Eg, + Pg, + Tg, + Gg, + Mg, + kg, + hg, + dag, + dg, + cg, + mg, + µg, + ng, + pg, + fg, + ag, + zg, + yg, + rg, + qg + "#; + test("pt", "SimpleSpeak", expr, + "quettagramas vírgula, ronnagramas vírgula, yottagramas vírgula, zettagramas vírgula, exagramas vírgula, petagramas vírgula, teragramas vírgula, gigagramas vírgula, megagramas vírgula, quilogramas vírgula, hectogramas vírgula, decagramas vírgula, decigramas vírgula, centigramas vírgula, miligramas vírgula, microgramas vírgula, nanogramas vírgula, picogramas vírgula, femtogramas vírgula, attogramas vírgula, zeptogramas vírgula, yoctogramas vírgula, rontogramas vírgula, quectogramas")?; + return Ok(()); + +} + +#[test] +fn si_base() -> Result<()> { + let expr = r#" + 1A,2A, + 1cd,2cd, + 1K,2K, + 1K,2K, + 1g,2g, + 1m,2m, + 1mol,2mol, + 1s,2s, + 1″,2″, + 1",2", + 1sec,2sec + "#; + test("pt", "SimpleSpeak", expr, + "1 ampère, vírgula; 2 ampères, vírgula; 1 candela, vírgula; 2 candelas, vírgula; 1 kelvin, vírgula; 2 kelvins, vírgula; 1 kelvin, vírgula; 2 kelvins, vírgula, 1 grama, vírgula; 2 gramas, vírgula, 1 metro, vírgula; 2 metros, vírgula, 1 mol, vírgula, 2 mols, vírgula; 1 segundo, vírgula; 2 segundos, vírgula; 1 segundo, vírgula; 2 segundos, vírgula; 1 segundo, vírgula; 2 segundos, vírgula; 1 segundo, vírgula; 2 segundos")?; + return Ok(()); + +} + +#[test] +fn si_base_with_prefixes() -> Result<()> { + let expr = r#" + 1QA,2RA, + 1Ycd,2Zcd, + 1EK,2PK, + 1TK,2GK, + 1Mg,2kg, + 1hm,2dam, + 1dmol,2cmol, + 1ms,2µs, + 1nsec,2psec + "#; + test("pt", "SimpleSpeak", expr, + "1 quettaampère, vírgula; 2 ronnaampères, vírgula; 1 yottacandela, vírgula; 2 zettacandelas, vírgula; 1 exakelvin, vírgula; 2 petakelvins, vírgula; 1 terakelvin, vírgula; 2 gigakelvins, vírgula; 1 megagrama, vírgula; 2 quilogramas, vírgula; 1 hectômetro, vírgula; 2 decâmetros, vírgula; 1 decimol, vírgula; 2 centimols, vírgula; 1 milissegundo, vírgula; 2 microssegundos, vírgula; 1 nanossegundo, vírgula; 2 picossegundos")?; + return Ok(()); + +} + + +#[test] +fn si_derived_1() -> Result<()> { + let expr = r#" + 1Bq,2Bq, + 1C,2C, + 1°C,2°C, + 1℃,2℃, + 1F,2F, + 1Gy,2Gy, + 1H,2H, + 1Hz,2Hz, + 1J,2J, + 1kat,2kat, + 1lm,2lm, + 1lx,2lx + "#; + test("pt", "SimpleSpeak", expr, + "1 becquerel, vírgula; 2 becquerels, vírgula; 1 coulomb, vírgula; 2 coulombs, vírgula; 1 grau Celsius, vírgula; 2 graus Celsius, vírgula; 1 grau Celsius, vírgula; 2 graus Celsius, vírgula, 1 farad, vírgula; 2 farads, vírgula, 1 gray, vírgula, 2 grays, vírgula, 1 henry, vírgula; 2 henrys, vírgula, 1 hertz, vírgula, 2 hertz, vírgula, 1 joule, vírgula; 2 joules, vírgula, 1 katal, vírgula; 2 katals, vírgula; 1 lúmen, vírgula; 2 lúmens, vírgula, 1 lux, vírgula, 2 lux")?; + return Ok(()); + +} + +#[test] +fn si_derived_1_with_prefixes() -> Result<()> { + let expr = r#" + 1QBq,2RBq, + 1YC,2ZC, + 1EF,2PF, + 1TGy,2GGy, + 1MH,2kH, + 1daHz,2dHz, + 1cJ,2mJ, + 1µkat,2nkat, + 1plm,2flm, + 1alx,2zlx, + 1m°C,2µ°C, + 1p℃,2n℃ + "#; + test("pt", "SimpleSpeak", expr, + "1 quettabecquerel, vírgula; 2 ronnabecquerels, vírgula; 1 yottacoulomb, vírgula; 2 zettacoulombs, vírgula; 1 exafarad, vírgula; 2 petafarads, vírgula; 1 teragray, vírgula; 2 gigagrays, vírgula; 1 megahenry, vírgula; 2 quilohenrys, vírgula; 1 decahertz, vírgula; 2 decihertz, vírgula; 1 centijoule, vírgula; 2 milijoules, vírgula; 1 microkatal, vírgula; 2 nanokatals, vírgula; 1 picolúmen, vírgula; 2 femtolúmens, vírgula; 1 attolux, vírgula; 2 zeptolux, vírgula; 1 miligrau Celsius, vírgula; 2 micrograus Celsius, vírgula; 1 picograu Celsius, vírgula; 2 nanograus Celsius")?; + return Ok(()); + +} + +#[test] +fn si_derived_2() -> Result<()> { + let expr = r#" + 1N,2N, + 1Ω,2Ω, + 1Ω,2Ω, + 1Pa,2Pa, + 1S,2S, + 1Sv,2Sv, + 1T,2T, + 1V,2V, + 1W,2W, + 1Wb,2Wb + "#; + test("pt", "SimpleSpeak", expr, + "1 newton, vírgula; 2 newtons, vírgula, 1 ohm, vírgula, 2 ohms, vírgula, 1 ohm, vírgula, 2 ohms, vírgula; 1 pascal, vírgula; 2 pascals, vírgula; 1 siemens, vírgula; 2 siemens, vírgula; 1 sievert, vírgula; 2 sieverts, vírgula, 1 tesla, vírgula; 2 teslas, vírgula, 1 volt, vírgula, 2 volts, vírgula, 1 watt, vírgula, 2 watts, vírgula, 1 weber, vírgula; 2 webers")?; + return Ok(()); + +} + +#[test] +fn si_derived_2_with_prefixes() -> Result<()> { + let expr = r#" + 1qN,2rN, + 1yΩ,2zΩ, + 1aΩ,2fΩ, + 1pPa,2nPa, + 1µS,2mS, + 1cSv,2dSv, + 1daT,2hT, + 1kV,2MV, + 1GW,2TW, + 1PWb,2EWb + "#; + test("pt", "SimpleSpeak", expr, + "1 quectonewton, vírgula; 2 rontonewtons, vírgula; 1 yoctoohm, vírgula; 2 zeptoohms, vírgula; 1 attoohm, vírgula; 2 femtoohms, vírgula; 1 picopascal, vírgula; 2 nanopascals, vírgula; 1 microssiemens, vírgula; 2 milissiemens, vírgula; 1 centissievert, vírgula; 2 decissieverts, vírgula; 1 decatesla, vírgula; 2 hectoteslas, vírgula; 1 quilovolt, vírgula; 2 megavolts, vírgula; 1 gigawatt, vírgula; 2 terawatts, vírgula; 1 petaweber, vírgula; 2 exawebers")?; + return Ok(()); + +} + + +#[test] +fn si_accepted() -> Result<()> { + let expr = r#" + 1l,2l, + 1L,2L, + 1ℓ,2ℓ, + 1t,2t, + 1Da,2Da, + 1Np,2Np, + 1u,2u, + 1eV,2eV, + 1rad,2rad, + 1sr,2sr, + 1a,2a, + 1as,2as, + 1b,2b, + 1B,2B, + 1Bd,2Bd + "#; + test("pt", "SimpleSpeak", expr, + "1 litro, vírgula; 2 litros, vírgula, 1 litro, vírgula; 2 litros, vírgula, 1 litro, vírgula; 2 litros, vírgula; 1 tonelada, vírgula; 2 toneladas, vírgula; 1 dalton, vírgula; 2 daltons, vírgula, 1 neper, vírgula; 2 neperes, vírgula; 1 unidade de massa atômica, vírgula; 2 unidades de massa atômica; vírgula; 1 elétron-volt, vírgula; 2 elétron-volts, vírgula; 1 radiano, vírgula; 2 radianos, vírgula; 1 esferorradiano, vírgula; 2 esferorradianos, vírgula, 1 ano, vírgula, 2 anos, vírgula; 1 segundo de arco, vírgula; 2 segundos de arco, vírgula, 1 bit, vírgula, 2 bits, vírgula, 1 byte, vírgula, 2 bytes, vírgula, 1 baud, vírgula, 2 bauds")?; + return Ok(()); + +} + +#[test] +fn si_accepted_with_prefixes() -> Result<()> { + let expr = r#" + 1Ql,2Rl, + 1YL,2ZL, + 1Eℓ,2Pℓ, + 1Tt,2Gt, + 1MDa,2kDa, + 1dNp,2cNp, + 1hu,2dau, + 1meV,2µeV, + 1nrad,2prad, + 1fsr,2asr, + 1Ga,2Ma, + 1zas,2yas, + 1kb,2Mb, + 1GB,2TB, + 1TBd,2EBd + "#; + test("pt", "SimpleSpeak", expr, + "1 quettalitro, vírgula; 2 ronnalitros, vírgula; 1 yottalitro, vírgula; 2 zettalitros, vírgula; 1 exalitro, vírgula; 2 petalitros, vírgula; 1 teratonelada, vírgula; 2 gigatoneladas, vírgula; 1 megadalton, vírgula; 2 quilodaltons, vírgula; 1 decineper, vírgula; 2 centineperes, vírgula; 1 hectounidade de massa atômica; vírgula; 2 decaunidades de massa atômica; vírgula; 1 milielétron-volt, vírgula; 2 microelétron-volts, vírgula; 1 nanorradiano, vírgula; 2 picorradianos, vírgula; 1 femtoesferorradiano, vírgula; 2 attoesferorradianos, vírgula; 1 gigaano, vírgula; 2 megaanos, vírgula; 1 zeptossegundo de arco, vírgula; 2 yoctossegundos de arco, vírgula; 1 quilobit, vírgula; 2 megabits, vírgula; 1 gigabyte, vírgula; 2 terabytes, vírgula; 1 terabaud, vírgula; 2 exabauds")?; + return Ok(()); + +} + +#[test] +fn without_prefix_time() -> Result<()> { + let expr = r#" + 1″,2″, + 1",2", + 1′,2′, + 1',2', + 1min,2min, + 1h,2h, + 1hr,2hr, + 1Hr,2Hr, + 1d,2d, + 1dy,2dy, + 1w,2w, + 1wk,2wk, + 1y,2y, + 1yr,2yr + "#; + test("pt", "SimpleSpeak", expr, + "1 segundo, vírgula; 2 segundos, vírgula; 1 segundo, vírgula; 2 segundos, vírgula; 1 minuto, vírgula; 2 minutos, vírgula; 1 minuto, vírgula; 2 minutos, vírgula; 1 minuto, vírgula; 2 minutos, vírgula, 1 hora, vírgula, 2 horas, vírgula, 1 hora, vírgula, 2 horas, vírgula, 1 hora, vírgula, 2 horas, vírgula, 1 dia, vírgula, 2 dias, vírgula, 1 dia, vírgula, 2 dias, vírgula; 1 semana, vírgula; 2 semanas, vírgula; 1 semana, vírgula; 2 semanas, vírgula, 1 ano, vírgula, 2 anos, vírgula, 1 ano, vírgula, 2 anos")?; + return Ok(()); + +} + +#[test] +fn without_prefix_angles() -> Result<()> { + let expr = r#" + 1°,2°, + 1deg,2deg, + 1arcmin,2arcmin, + 1amin,2amin, + 1am,2am, + 1MOA,2MOA, + 1arcsec,2arcsec, + 1asec,2asec + "#; + test("pt", "SimpleSpeak", expr, + "1 grau, vírgula, 2 graus, vírgula, \ + 1 grau, vírgula, 2 graus, vírgula; \ + 1 minuto de arco, vírgula; 2 minutos de arco, vírgula; \ + 1 minuto de arco, vírgula; 2 minutos de arco, vírgula; \ + 1 minuto de arco, vírgula; 2 minutos de arco, vírgula; \ + 1 minuto de arco, vírgula; 2 minutos de arco, vírgula; \ + 1 segundo de arco, vírgula; 2 segundos de arco, vírgula; \ + 1 segundo de arco, vírgula; 2 segundos de arco")?; + return Ok(()); + +} + +#[test] +fn without_prefix_distance() -> Result<()> { + let expr = r#" + 1au,2au, + 1ltyr,2ltyr, + 1pc,2pc, + 1Å,2Å, + 1Å,2Å, + 1fm,2fm + "#; + test("pt", "SimpleSpeak", expr, + "1 unidade astronômica, vírgula; 2 unidades astronômicas, vírgula; 1 ano-luz, vírgula; 2 anos-luz, vírgula; 1 parsec, vírgula; 2 parsecs, vírgula; 1 angström, vírgula; 2 angströms, vírgula; 1 angström, vírgula; 2 angströms, vírgula, 1 fermi, vírgula; 2 fermis")?; + return Ok(()); + +} + +#[test] +fn without_prefix_other() -> Result<()> { + let expr = r#" + 1ha,2ha, + 1dB,2dB, + 1atm,2atm, + 1amu,2amu, + 1bar,2bar, + 1cal,2cal, + 1Ci,2Ci, + 1grad,2grad, + 1M,2M, + 1R,2R, + 1rpm,2rpm, + 1℧,2℧, + 1dyn,2dyn, + 1erg,2erg + "#; + test("pt", "SimpleSpeak", expr, + "1 hectare, vírgula; 2 hectares, vírgula; 1 decibel, vírgula; 2 decibéis, vírgula; 1 atmosfera, vírgula; 2 atmosferas, vírgula; 1 unidade de massa atômica, vírgula; 2 unidades de massa atômica; vírgula, 1 bar, vírgula, 2 bares, vírgula; 1 caloria, vírgula; 2 calorias, vírgula, 1 curie, vírgula; 2 curies, vírgula, 1 grado, vírgula; 2 grados, vírgula, 1 molar, vírgula; 2 molares, vírgula; 1 roentgen, vírgula; 2 roentgens, vírgula; 1 rotação por minuto, vírgula; 2 rotações por minuto, vírgula, 1 mho, vírgula, 2 mhos, vírgula, 1 dina, vírgula, 2 dinas, vírgula, 1 erg, vírgula, 2 ergs")?; + return Ok(()); + +} + +#[test] +fn metro() -> Result<()> { + // this is a special case in Spanish + let expr = r#" + 1m, + 2m, + 1Gm, + 2Gm, + 1Mm, + 2Mm, + 1km, + 2km, + 1hm, + 2hm, + 1dam, + 2dam, + 1mm, + 2mm + "#; + test("pt", "SimpleSpeak", expr, + "1 metro, vírgula; 2 metros, vírgula; 1 gigâmetro, vírgula; 2 gigâmetros, vírgula; 1 megâmetro, vírgula; 2 megâmetros, vírgula; 1 quilômetro, vírgula; 2 quilômetros, vírgula; 1 hectômetro, vírgula; 2 hectômetros, vírgula; 1 decâmetro, vírgula; 2 decâmetros, vírgula; 1 milímetro, vírgula; 2 milímetros")?; + return Ok(()); + +} + +#[test] +fn without_prefix_powers_of_2() -> Result<()> { + let expr = r#" + 1Kib,2Kib, + 1Mib,2Mib, + 1Gib,2Gib, + 1Tib,2Tib, + 1Pib,2Pib, + 1Eib,2Eib, + 1Zib,2Zib, + 1Yib,2Yib, + 1KiB,2KiB, + 1MiB,2MiB, + 1GiB,2GiB, + 1TiB,2TiB, + 1PiB,2PiB, + 1EiB,2EiB, + 1ZiB,2ZiB, + 1YiB,2YiB + "#; + test("pt", "SimpleSpeak", expr, + "1 kibibit, vírgula; 2 kibibits, vírgula; 1 mebibit, vírgula; 2 mebibits, vírgula; 1 gibibit, vírgula; 2 gibibits, vírgula; 1 tebibit, vírgula; 2 tebibits, vírgula; 1 pebibit, vírgula; 2 pebibits, vírgula; 1 exbibit, vírgula; 2 exbibits, vírgula; 1 zebibit, vírgula; 2 zebibits, vírgula; 1 yobibit, vírgula; 2 yobibits, vírgula; 1 kibibyte, vírgula; 2 kibibytes, vírgula; 1 mebibyte, vírgula; 2 mebibytes, vírgula; 1 gibibyte, vírgula; 2 gibibytes, vírgula; 1 tebibyte, vírgula; 2 tebibytes, vírgula; 1 pebibyte, vírgula; 2 pebibytes, vírgula; 1 exbibyte, vírgula; 2 exbibytes, vírgula; 1 zebibyte, vírgula; 2 zebibytes, vírgula; 1 yobibyte, vírgula; 2 yobibytes")?; + return Ok(()); + +} + + +#[test] +fn si_other_numbers() -> Result<()> { + let expr = r#"1.0l, + 2.0 m, + x ms, + yµs, + dag, + 1235daN, + 2.5µsec, + 32.34mol"#; + test("pt", "SimpleSpeak", expr, + "10 litro, vírgula; 20 metros, vírgula; x milissegundos, vírgula; y microssegundos, vírgula, decagramas vírgula; 1235 decanewtons, vírgula; 25 microssegundos, vírgula; 3234 mols")?; + return Ok(()); + +} + + +#[test] +fn test_mtext_inference() -> Result<()> { + let expr = r#"[ + 1t, + 2PA, + 3Pa, + 4.5mT + ]"#; + test("pt", "SimpleSpeak", expr, + "abre colchetes; 1 tonelada, vírgula; 2 petaampères, vírgula; 3 pascals, vírgula; 45 militeslas; fecha colchetes")?; + return Ok(()); + +} + diff --git a/tests/languages.rs b/tests/languages.rs index b0552b95b..287b53652 100644 --- a/tests/languages.rs +++ b/tests/languages.rs @@ -12,6 +12,7 @@ mod Languages { mod fi; mod pl; mod sv; + mod pt; mod nb; mod de; mod fr; From aaad7468e1fb0bc266a5855253bfcd7d6561ed18 Mon Sep 17 00:00:00 2001 From: NSoiffer Date: Wed, 2 Sep 2026 06:39:05 -0700 Subject: [PATCH 084/120] Release candidate (#743) * Add UEB G1 BANA/GTM mode preference and expand Grade-2 contractions. Adding the preference is a stall tactic while I wait for feedback from ICEB about whether BANA rules are obsolete. Introduce UEB_G1_BANA_Style (default true) to choose BANA 2019 vs ICEB GTM 1.7.3/1.7.5 grade-1 indicator placement in ueb_cleanup. Implement GTM 1.7 per-symbols-sequence counting with gtm_1_7_mode, and add matching ICEB tests. Extend handle_contractions for technical vocabulary: UEB 10.8 final-letter groupsigns, dot-5 through/part, dis/con prefixes, cc/ea exception words, and a ful-pattern fix. Add ueb_technical_word_contractions (311 words, goldens cross-checked with brailletranslators.com). Wire the preference through prefs, fuzz, and docs. * updated to new BANA and ICEB spec updates. Five tests fail. Still working on them * removed an option * fix contractions involving 'be, con, dis' which can only be at the start of a 'word' (where 'word' has a specific UEB meaning) * Updated to UEB to the latest guidance for grade 1 indicators. This adds new tests and changes a few tests. Also added more contractions. * update rc # * fix new clippy warnings * fix up Vietanmese tests * fix str compile issue with no-unsafe feature --- AGENTS.md | 3 +- Cargo.toml | 2 +- PythonScripts/audit_translations/README.md | 11 +- PythonScripts/audit_translations/auditor.py | 34 +- PythonScripts/audit_translations/cli.py | 14 +- .../tests/test_cli_end_to_end.py | 74 - Rules/Braille/Nemeth/unicode-full.yaml | 2728 ++++++++--------- Rules/Braille/UEB/UEB_Rules.yaml | 9 +- Rules/prefs.yaml | 1 + docs/users.md | 42 +- src/braille.rs | 980 +++++- tests/braille/UEB/iceb.rs | 1103 ++++++- tests/braille/UEB/other.rs | 21 +- 13 files changed, 3176 insertions(+), 1846 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c6ca9e756..7093cf698 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,7 +13,7 @@ but add common mistakes of AI agents here instead. - `ClearSpeak_Rules.yaml`, `SimpleSpeak_Rules.yaml` - `SharedRules/`, `unicode.yaml`, `unicode-full.yaml`, `definitions.yaml`, `navigate.yaml` - `build.rs` can bundle rules into `rules.zip` when `include-zip` is enabled (always the minimized tree). Shared zipper: `src/rules_archive.rs`, invoked as `cargo run --bin package-rules -- Rules [--minimize]`. -- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (flow-style rewrite of all YAML: comments dropped). Each language/braille subdir is stored as `/.zip`; top-level and `Intent/` YAML stay loose. Inner language zips use BZIP2; the outer downloadable archive uses DEFLATE so `unzip` works. Both omit `Languages/zz`; the `test-rules-package` job restores `zz` from git after unzip so unit tests still run against release-like archives. +- CI packages `Rules.zip` (verbatim) and `Rules-minimized.zip` (comments stripped from `Languages/**/unicode.yaml` and `unicode-full.yaml` only). Each language/braille subdir is stored as `/.zip`; top-level and `Intent/` YAML stay loose. Inner language zips use BZIP2; the outer downloadable archive uses DEFLATE so `unzip` works. Both omit `Languages/zz`; the `test-rules-package` job restores `zz` from git after unzip so unit tests still run against release-like archives. ## Translation Conventions - `t:` means untranslated or unverified. @@ -33,6 +33,7 @@ but add common mistakes of AI agents here instead. - Do not mirror README content here; keep guidance agent-specific. - Avoid broad formatting sweeps; do not run `cargo fmt` in this repo. - Keep code/rule changes focused and validate with targeted tests first: `cargo test ` +- Do **not** change test goldens (expected braille/speech/nav strings) unless the user explicitly says to. Prefer fixing code/rules to match existing goldens; if a golden looks wrong vs the spec, ask before editing it. - do not do any git commands unless explicitly asked for - Rust coverage is in `target/coverage/`. - When working with GitHub, e.g. looking at PRs and issues, check if the GitHub CLI is installed (`gh --version`). diff --git a/Cargo.toml b/Cargo.toml index fcf5428d9..2e73cbf80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mathcat" -version = "0.7.7-alpha.1" +version = "0.7.6-rc.4" authors = ["Neil Soiffer "] license = "MIT" description = "MathCAT: Math Capable Assistive Technology ('Speech and braille from MathML')" diff --git a/PythonScripts/audit_translations/README.md b/PythonScripts/audit_translations/README.md index fd399e412..8aedf1265 100644 --- a/PythonScripts/audit_translations/README.md +++ b/PythonScripts/audit_translations/README.md @@ -77,7 +77,6 @@ uv run --project PythonScripts audit-translations --list * Region variants are shown as `lang-region` (e.g., `zz-aa`) based on subdirectories under `Rules/Languages/`. * `--source`: Sets the source/reference language. Defaults to `en`. * `--file`: Audits a single specific file instead of the whole directory. -* `--exclude`: Exclude one or more files from the audit. * `--rules-dir`: Override the Rules/Languages directory path. * `--only`: Filter issue types (comma-separated): `missing`, `untranslated`, `extra`, `diffs`, `all`. * `--verbose`: Show detailed output including source/target snippets for rule differences. @@ -101,17 +100,9 @@ uv run audit-translations de # Compare Norwegian Bokmal against Swedish instead of English uv run audit-translations nb --source sv -# Audit only a specific file (note: --file is incompatible with --exclude) -uv run audit-translations es --file ClearSpeak_Rules.yaml +# Audit only a specific file uv run audit-translations es --file SharedRules/default.yaml -# Exclude a list of files from the audit, note that if you use this option before specifying the -# target language you'll need to use the option terminator (--). -# (note: --exclude is incompatible with --file) -uv run audit-translations es --exclude unicode-full.yaml -uv run audit-translations es --exclude unicode-full.yaml unicode.yaml -uv run audit-translations --exclude unicode-full.yaml -- es - # Audit a regional variant (merges Rules/Languages/de and Rules/Languages/de/CH) uv run audit-translations de-CH diff --git a/PythonScripts/audit_translations/auditor.py b/PythonScripts/audit_translations/auditor.py index 37ec44f13..b67cbf911 100644 --- a/PythonScripts/audit_translations/auditor.py +++ b/PythonScripts/audit_translations/auditor.py @@ -46,31 +46,24 @@ def is_definitions_file(file_path: str | Path) -> bool: return Path(file_path).name == "definitions.yaml" -def get_yaml_files(lang_dir: Path, region_dir: Path | None = None, excluded_files: list[str] | None = None) -> list[Path]: +def get_yaml_files(lang_dir: Path, region_dir: Path | None = None) -> list[Path]: """Get all YAML files to audit for a language, including region overrides.""" files: set[Path] = set() - def collect_from(directory: Path, root: Path, excluded: list[str] | None) -> None: + def collect_from(directory: Path, root: Path) -> None: if not directory.exists(): return - - excluded_paths = {Path(path) for path in excluded or []} - - candidates = {f for f in directory.glob("*.yaml") if f.name != "prefs.yaml"} - candidates.update((directory / "SharedRules").glob("*.yaml")) - - relative_candidates = {f.relative_to(directory): f for f in candidates} - - for path in excluded_paths - relative_candidates.keys(): - console.print(f"\n[yellow]⚠ Warning:[/] File to exclude {path} does not exist.") - - files.update( - f.relative_to(root) for relative_path, f in relative_candidates.items() if relative_path not in excluded_paths - ) - - collect_from(lang_dir, lang_dir, excluded_files) + for f in directory.glob("*.yaml"): + if f.name != "prefs.yaml": + files.add(f.relative_to(root)) + shared_dir = directory / "SharedRules" + if shared_dir.exists(): + for f in shared_dir.glob("*.yaml"): + files.add(f.relative_to(root)) + + collect_from(lang_dir, lang_dir) if region_dir: - collect_from(region_dir, region_dir, excluded_files) + collect_from(region_dir, region_dir) return sorted(files) @@ -222,7 +215,6 @@ def merge_definitions( def audit_language( language: str, specific_file: str | None = None, - excluded_files: list[str] | None = None, rules_dir: str | None = None, issue_filter: set[str] | None = None, verbose: bool = False, @@ -256,7 +248,7 @@ def audit_language( raise AuditError(f"Target region directory not found: {translated_region_dir}") # Get list of files to audit - files = [specific_file] if specific_file else get_yaml_files(source_dir, source_region_dir, excluded_files) + files = [specific_file] if specific_file else get_yaml_files(source_dir, source_region_dir) print_audit_header(language, len(files), source_language) diff --git a/PythonScripts/audit_translations/cli.py b/PythonScripts/audit_translations/cli.py index 5bdf30e6d..6916a636e 100644 --- a/PythonScripts/audit_translations/cli.py +++ b/PythonScripts/audit_translations/cli.py @@ -30,18 +30,7 @@ def main() -> None: parser.add_argument("language", nargs="?", help="Language code to audit (e.g., 'es', 'de', 'fi')") parser.add_argument("--source", default="en", help="Source/reference language code (default: 'en')") - file_group = parser.add_mutually_exclusive_group() - file_group.add_argument( - "--file", - dest="specific_file", - help="Audit only a specific file (e.g., 'SharedRules/default.yaml')", - ) - file_group.add_argument( - "--exclude", - nargs="+", - dest="excluded_files", - help="Exclude a list of files from the audit.", - ) + parser.add_argument("--file", dest="specific_file", help="Audit only a specific file (e.g., 'SharedRules/default.yaml')") parser.add_argument("--list", action="store_true", help="List available languages") parser.add_argument("--rules-dir", help="Override Rules/Languages directory path") parser.add_argument( @@ -78,7 +67,6 @@ def main() -> None: audit_language( args.language, args.specific_file, - args.excluded_files, args.rules_dir, issue_filter, args.verbose, diff --git a/PythonScripts/audit_translations/tests/test_cli_end_to_end.py b/PythonScripts/audit_translations/tests/test_cli_end_to_end.py index 40cf3f8b2..7876bc0de 100644 --- a/PythonScripts/audit_translations/tests/test_cli_end_to_end.py +++ b/PythonScripts/audit_translations/tests/test_cli_end_to_end.py @@ -127,80 +127,6 @@ def test_cli_main_rich_only_filters_issue_groups(capsys, monkeypatch) -> None: assert "Structure Differences" not in output -@pytest.mark.parametrize( - ("excluded_file", "expected_file", "expected_rule", "unexpected_rule"), - [ - ( - "default.yaml", - "SharedRules/default.yaml", - "shared-only", - "root-only", - ), - ( - "SharedRules/default.yaml", - "default.yaml", - "root-only", - "shared-only", - ), - ], - ids=["exclude-root", "exclude-shared-rules"], -) -def test_cli_main_exclude_uses_relative_paths( - tmp_path, - capsys, - monkeypatch, - excluded_file, - expected_file, - expected_rule, - unexpected_rule, -) -> None: - """Ensure --exclude distinguishes root files from SharedRules files.""" - rules_dir = tmp_path / "Rules" / "Languages" - source_dir = rules_dir / "en" - target_dir = rules_dir / "de" - - (source_dir / "SharedRules").mkdir(parents=True) - (target_dir / "SharedRules").mkdir(parents=True) - - (source_dir / "default.yaml").write_text( - '- name: root-only\n tag: mo\n match: ".//m:mi"\n replace:\n - t: "root"\n', - encoding="utf-8", - ) - (source_dir / "SharedRules" / "default.yaml").write_text( - '- name: shared-only\n tag: mo\n match: ".//m:mi"\n replace:\n - t: "shared"\n', - encoding="utf-8", - ) - - target_rule = '- name: target-only\n tag: mo\n match: ".//m:mi"\n replace:\n - t: "target"\n' - (target_dir / "default.yaml").write_text( - target_rule, - encoding="utf-8", - ) - (target_dir / "SharedRules" / "default.yaml").write_text( - target_rule, - encoding="utf-8", - ) - - args = [ - "de", - "--rules-dir", - str(rules_dir), - "--exclude", - excluded_file, - "--only", - "missing", - ] - monkeypatch.setattr(sys, "argv", ["audit_translations", *args]) - - audit_cli.main() - output = strip_ansi(capsys.readouterr().out) - - assert "Files to check: 1" in output - assert expected_file in output - assert expected_rule in output - assert unexpected_rule not in output - - def test_cli_main_accepts_source_language(capsys, monkeypatch) -> None: """ Ensure --source changes the reference language without changing target semantics. diff --git a/Rules/Braille/Nemeth/unicode-full.yaml b/Rules/Braille/Nemeth/unicode-full.yaml index bf4f842ea..30ffc561d 100644 --- a/Rules/Braille/Nemeth/unicode-full.yaml +++ b/Rules/Braille/Nemeth/unicode-full.yaml @@ -1,1364 +1,1364 @@ ---- -# To greatly simplify typeface/language generation, the chars have unique ASCII chars for them: -# Typeface: S: sans-serif, B: bold, T: script/blackboard, I: italic, R: Roman -# Language: E: English, D: German, G: Greek, V: Greek variants, H: Hebrew, U: Russian -# Indicators: C: capital, L: letter, N: number, P: punctuation, M: multipurpose -# Others: W -- whitespace that should be kept (e.g, in a numeral) -# SRE doesn't have H: Hebrew, so not encoded (yet) -# Some of the weird letters (e.g., circled letters) I didn't translate with the above because -# they don't interact with rules since they are enclosed in other chars. - - - "⨯": [t: "⠈⠡"] # U+2A2F(VECTOR OR CROSS PRODUCT) -- make the same as 0x00D7 (Multiplication sign) - - # --- Nemeth Default Alphabet bold chars. --- - - "𝐀-𝐙": # 0x1d400 - 0x1d419 - - tc: "BE" - - spell: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - "𝐚-𝐳": # 0x1d41a - 0x1d433 - - tc: "BE" - - spell: "translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 - - tc: "BG" - - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝛂-𝛒": # 0x1d6c2 - 0x1d6d2 - - tc: "BG" - - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒', 'αβγδεζηθικλμνξοπρ')" - - - "𝛔-𝛖": # 0x1d6d4 - 0x1d6d6 - - tc: "BG" - - spell: "translate('.', '𝛔𝛕𝛖', 'στυ')" - - - "𝛘-𝛚": # 0x1d6d8 - 0x1d6da - - tc: "BG" - - spell: "translate('.', '𝛘𝛙𝛚', 'χψω')" - - - "𝛁": [tc: "BGL⠫"] # 0x1d6c1 - - "𝛓": [tc: "BVL⠎"] # 0x1d6d3 - - "𝛗": [tc: "BVL⠋"] # 0x1d6d7 - - "𝛛": [tc: "⠸⠈⠙"] # 0x1d6db - - "𝛜": [tc: "BGL⠑"] # 0x1d6dc - - "𝛝": [tc: "BVL⠹"] # 0x1d6dd - - "𝛞": [tc: "BGL⠅"] # 0x1d6de - - "𝛟": [tc: "BGL⠋"] # 0x1d6df - - "𝛠": [tc: "BGL⠗"] # 0x1d6e0 - - "𝛡": [tc: "BGL⠏"] # 0x1d6e1 - - - "𝟎-𝟗": # 0x1d7ce - 0x1d7d7 - - tc: "B" - - spell: "translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789')" - - # --- Nemeth Default Alphabet bold-fraktur chars. --- - - - "𝕬-𝖅": # 0x1d56c - 0x1d585 - - tc: "BD" - - spell: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝖆-𝖟": # 0x1d586 - 0x1d59f - - tc: "BD" - - spell: "translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz')" - - # --- Nemeth Default Alphabet bold-italic chars. --- - - "𝑨-𝒁": # 0x1d468 - 0x1d481 - - tc: "BE" - - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝒂-𝒛": # 0x1d482 - 0x1d49b - - tc: "BE" - - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝜜-𝜴": # 0x1d71c - 0x1d734 - - tc: "BG" - - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳𝜴', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝜵": [tc: "BGL⠫"] # 0x1d735 - - - "𝜶-𝝆": # 0x1d736 - 0x1d746 - - tc: "BG" - - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆', 'αβγδεζηθικλμνξοπρ')" - - - "𝝇": [tc: "BVL⠎"] # 0x1d747 - - - "𝝈-𝝊": # 0x1d748 - 0x1d74a - - tc: "BG" - - spell: "translate('.', '𝝈𝝉𝝊', 'στυ')" - - - "𝝋": [tc: "BVL⠋"] # 0x1d74b - - - "𝝌-𝝎": # 0x1d74c - 0x1d74e - - tc: "BG" - - spell: "translate('.', '𝝌𝝍𝝎', 'χψω')" - - - "𝝏": [tc: "BVL⠙"] # 0x1d74f - - "𝝐": [tc: "BGL⠑"] # 0x1d750 - - "𝝑": [tc: "BVL⠹"] # 0x1d751 - - - "𝝒-𝝕": # 0x1d752 - 0x1d755 - - tc: "BG" - - spell: "translate('.', '𝝒𝝓𝝔𝝕', 'κφρπ')" - - # --- Nemeth Default Alphabet bold-script chars. --- - - - "𝓐-𝓩": # 0x1d4d0 - 0x1d4e9 - - tc: "BTE" - - spell: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝓪-𝔃": # 0x1d4ea - 0x1d503 - - tc: "BTE" - - spell: "translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝚤-𝚥": # 0x1d6a4 - 0x1d6a5 - - tc: "" - - spell: "translate('.', '𝚤𝚥', '⠊⠚')" - -# Russian Alphabet (from Nemeth book) -# NOTE: is this translation needed? - - "А-Я": # 0x410 - 0x42f - - tc: "U" - - spell: "translate('.', 'АБВГДЕЗИЙКЛМНОПРСТУФЭ', 'ABVGDEZIYKLMNOPRSTUFE')" - - - "а-я": # 0x430 - 0x44f - - tc: "U" - - spell: "translate('.', 'абвгдезийклмнопрстуфэ', 'abvgdeziylkmnoprstufye')" - - - "①-⑨": # 0x2460 - 0x2473 - - tc: "⠫⠉⠸⠫⠼" - - spell: "translate('.', '①②③④⑤⑥⑦⑧⑨', '123456789')" - - tc: "⠻" - - - "⓪": [tc: "⠫⠉⠸⠫⠼⠴⠻"] # 0x24ea - - - "⑩": [tc: "⠫⠉⠸⠫⠼⠂⠴⠻"] # 0x2469 - - "⑪": [tc: "⠫⠉⠸⠫⠼⠂⠂⠻"] # 0x246a - - "⑫": [tc: "⠫⠉⠸⠫⠼⠂⠆⠻"] # 0x246b - - "⑬": [tc: "⠫⠉⠸⠫⠼⠂⠒⠻"] # 0x246c - - "⑭": [tc: "⠫⠉⠸⠫⠼⠂⠲⠻"] # 0x246d - - "⑮": [tc: "⠫⠉⠸⠫⠼⠂⠢⠻"] # 0x246e - - "⑯": [tc: "⠫⠉⠸⠫⠼⠂⠖⠻"] # 0x246f - - "⑰": [tc: "⠫⠉⠸⠫⠼⠂⠶⠻"] # 0x2470 - - "⑱": [tc: "⠫⠉⠸⠫⠼⠂⠦⠻"] # 0x2471 - - "⑲": [tc: "⠫⠉⠸⠫⠼⠂⠔⠻"] # 0x2472 - - "⑳": [tc: "⠫⠉⠸⠫⠼⠆⠴⠻"] # 0x2473 - - - "㉑": [tc: "⠫⠉⠸⠫⠼⠆⠂⠻"] # 0x3251 - - "㉒": [tc: "⠫⠉⠸⠫⠼⠆⠆⠻"] # 0x3252 - - "㉓": [tc: "⠫⠉⠸⠫⠼⠆⠒⠻"] # 0x3253 - - "㉔": [tc: "⠫⠉⠸⠫⠼⠆⠲⠻"] # 0x3254 - - "㉕": [tc: "⠫⠉⠸⠫⠼⠆⠢⠻"] # 0x3255 - - "㉖": [tc: "⠫⠉⠸⠫⠼⠆⠖⠻"] # 0x3256 - - "㉗": [tc: "⠫⠉⠸⠫⠼⠆⠶⠻"] # 0x3257 - - "㉘": [tc: "⠫⠉⠸⠫⠼⠆⠦⠻"] # 0x3258 - - "㉙": [tc: "⠫⠉⠸⠫⠼⠆⠔⠻"] # 0x3259 - - "㉚": [tc: "⠫⠉⠸⠫⠼⠒⠴⠻"] # 0x325a - - "㉛": [tc: "⠫⠉⠸⠫⠼⠒⠂⠻"] # 0x325b - - "㉜": [tc: "⠫⠉⠸⠫⠼⠒⠆⠻"] # 0x325c - - "㉝": [tc: "⠫⠉⠸⠫⠼⠒⠒⠻"] # 0x325d - - "㉞": [tc: "⠫⠉⠸⠫⠼⠒⠲⠻"] # 0x325e - - "㉟": [tc: "⠫⠉⠸⠫⠼⠒⠢⠻"] # 0x325f - - "㊱": [tc: "⠫⠉⠸⠫⠼⠒⠖⠻"] # 0x32b1 - - "㊲": [tc: "⠫⠉⠸⠫⠼⠒⠶⠻"] # 0x32b2 - - "㊳": [tc: "⠫⠉⠸⠫⠼⠒⠦⠻"] # 0x32b3 - - "㊴": [tc: "⠫⠉⠸⠫⠼⠒⠔⠻"] # 0x32b4 - - "㊵": [tc: "⠫⠉⠸⠫⠼⠲⠴⠻"] # 0x32b5 - - "㊶": [tc: "⠫⠉⠸⠫⠼⠲⠂⠻"] # 0x32b6 - - "㊷": [tc: "⠫⠉⠸⠫⠼⠲⠆⠻"] # 0x32b7 - - "㊸": [tc: "⠫⠉⠸⠫⠼⠲⠒⠻"] # 0x32b8 - - "㊹": [tc: "⠫⠉⠸⠫⠼⠲⠲⠻"] # 0x32b9 - - "㊺": [tc: "⠫⠉⠸⠫⠼⠲⠢⠻"] # 0x32ba - - "㊻": [tc: "⠫⠉⠸⠫⠼⠲⠖⠻"] # 0x32bb - - "㊼": [tc: "⠫⠉⠸⠫⠼⠲⠶⠻"] # 0x32bc - - "㊽": [tc: "⠫⠉⠸⠫⠼⠲⠦⠻"] # 0x32bd - - "㊾": [tc: "⠫⠉⠸⠫⠼⠲⠔⠻"] # 0x32be - - "㊿": [tc: "⠫⠉⠸⠫⠼⠢⠴⠻"] # 0x32bf - -#NOTE: uncertainty here - - "Ⓐ-Ⓩ": # 0x24b6 - 0x24cf - - tc: "⠫⠉⠸⠫⠠" - - spell: "translate('.', 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - tc: "⠻" - - - "ⓐ-ⓩ": # 0x24d0 - 0x24e9 - - tc: "⠫⠉⠸⠫" - - spell: "translate('.', 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ', 'abcdefghijklmnopqrstuvwxyz')" - - tc: "⠻" - - # --- Nemeth Default Alphabet circled-sans-serif chars. --- - - - "➀-➈": # 0x1f10b, 0x2780 - 0x2788 - - tc: "⠫⠉⠸⠫⠠⠨⠼" - - spell: "translate('.', '➀➁➂➃➄➅➆➇➈', '123456789')" - - tc: "⠻" - - - "🄋": [tc: "⠫⠉⠸⠫⠠⠨⠼⠴⠻"] # 0x1f10b - - "➉": [tc: "⠫⠉⠸⠫⠠⠨⠼⠂⠴⠻"] # 0x2789 - - # --- Nemeth Default Alphabet comma chars. --- - - - "🄁-🄊": # 0x1f101 - 0x1f10a - - spell: "translate('.', '🄁🄂🄃🄄🄅🄆🄇🄈🄉🄊', '0123456789')" - - tc: "⠠" - - - # --- Nemeth Default Alphabet double-circled chars. --- - - - "⓵": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠂⠻⠻"] # 0x24f5 - - "⓶": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠆⠻⠻"] # 0x24f6 - - "⓷": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠒⠻⠻"] # 0x24f7 - - "⓸": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠲⠻⠻"] # 0x24f8 - - "⓹": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠢⠻⠻"] # 0x24f9 - - "⓺": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠖⠻⠻"] # 0x24fa - - "⓻": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠶⠻⠻"] # 0x24fb - - "⓼": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠦⠻⠻"] # 0x24fc - - "⓽": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠔⠻⠻"] # 0x24fd - - "⓾": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠂⠴⠻⠻"] # 0x24fe - - # --- Nemeth Default Alphabet double-struck chars. --- - - "𝔸-𝕐": # 0x1d538 - 0x1d550 (partial, excludes ℂ ℍ ℕ ℙ ℚ ℝ ℤ) - - tc: "𝔹E" - - spell: "translate('.', '𝔸𝔹𝔻𝔼𝔽𝔾𝕀𝕁𝕂𝕃𝕄𝕆𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'ABDEFGIJKLMOSTUVWXY')" - - - "ℂ": [tc: "𝔹ECL⠉"] # 0x2102 - - "ℍ": [tc: "𝔹ECL⠓"] # 0x210d - - "ℕ": [tc: "𝔹ECL⠝"] # 0x2115 - - "ℙ": [tc: "𝔹ECL⠏"] # 0x2119 - - "ℚ": [tc: "𝔹ECL⠟"] # 0x211a - - "ℝ": [tc: "𝔹ECL⠗"] # 0x211d - - "ℤ": [tc: "𝔹ECL⠵"] # 0x2124 - - "Ω": [t: "CGL⠺"] # 0x2126 (Ohm sign (capital Greek omega)) - - - "𝕒-𝕫": # 0x1d552 - 0x1d56b - - tc: "𝔹E" - - spell: "translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝟘-𝟡": # 0x1d7d8 - 0x1d7e1 - - tc: "𝔹" - - spell: "translate('.', '𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡', '0123456789')" - - - # --- Nemeth Default Alphabet fraktur chars. --- - - - "𝔄-𝔜": # 0x1d504 - 0x1d51c - - tc: "D" - - spell: "translate('.', '𝔄𝔅𝔇𝔈𝔉𝔊𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔖𝔗𝔘𝔙𝔚𝔛𝔜', 'ABDEFGJKLMNOPQSTUVWXY')" - - - "ℭ": [tc: "DCL⠉"] # 0x212d - - "ℌ": [tc: "DCL⠓"] # 0x210c - - "ℑ": [tc: "DCL⠊"] # 0x2111 - - "ℜ": [tc: "DCL⠗"] # 0x211c - - "ℨ": [tc: "DCL⠵"] # 0x2128 - - - "𝔞-𝔷": # 0x1d51e - 0x1d537 - - tc: "D" - - spell: "translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz')" - - # --- Nemeth Default Alphabet fullwidth chars. --- - - - "A-Z": # 0xff21 - 0xff3a - - tc: "" - - spell: "translate('.', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "a-z": # 0xff41 - 0xff5a - - tc: "" - - spell: "translate('.', 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz')" - - - "0-9": # 0xff10 - 0xff19 - - tc: "" - - spell: "translate('.', '0123456789', '0123456789')" - - # --- Nemeth Default Alphabet italic chars -- treat as non-italic. --- - - - "𝐴-𝑍": # 0x1d434 - 0x1d44d - - tc: "E" - - spell: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝑎-𝑧": # 0x1d44e - 0x1d467 - - tc: "E" - - spell: "translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔ℎ𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝛢-𝛺": # 0x1d6e2 - 0x1d6fa - - tc: "G" - - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹𝛺', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝛻": [tc: "GL⠫"] # 0x1d6fb - - - "𝛼-𝜌": # 0x1d6fc - 0x1d70c - - tc: "G" - - spell: "translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌', 'αβγδεζηθικλμνξοπρ')" - - "𝜍": [tc: "VL⠎"] # 0x1d70d - - - "𝜎": [tc: "GL⠎"] # 0x1d70e - - "𝜏": [tc: "GL⠞"] # 0x1d70f - - "𝜐": [tc: "GL⠥"] # 0x1d710 - - - "𝜑": [tc: "VL⠋"] # 0x1d711 - - - "𝜒": [tc: "GL⠯"] # 0x1d712 - - "𝜓": [tc: "GL⠽"] # 0x1d713 - - "𝜔": [tc: "GL⠺"] # 0x1d714 - - - "𝜕": [tc: "VL⠙"] # 0x1d715 - - - "𝜖": [tc: "GL⠑"] # 0x1d716 - - - "𝜗": [tc: "VL⠹"] # 0x1d717 - - - "𝜘": [tc: "GL⠅"] # 0x1d718 - - "𝜙": [tc: "GL⠋"] # 0x1d719 - - "𝜚": [tc: "GL⠗"] # 0x1d71a - - "𝜛": [tc: "GL⠏"] # 0x1d71b - - # --- Nemeth Default Alphabet monospace chars. --- - - - "𝙰-𝚉": # 0x1d670 - 0x1d689 - - tc: "" - - spell: "translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝚊-𝚣": # 0x1d68a - 0x1d6a3 - - tc: "" - - spell: "translate('.', '𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝟶-𝟿": # 0x1d7f6 - 0x1d7ff - - tc: "" - - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" - - - # --- Nemeth Default Alphabet negative-circled chars. --- - - "⓿": [t: "⠫⠸⠉⠸⠫⠼⠴⠻"] # 0x24ff - - "❶": [t: "⠫⠸⠉⠸⠫⠼⠂⠻"] # 0x2776 - - "❷": [t: "⠫⠸⠉⠸⠫⠼⠆⠻"] # 0x2777 - - "❸": [t: "⠫⠸⠉⠸⠫⠼⠒⠻"] # 0x2778 - - "❹": [t: "⠫⠸⠉⠸⠫⠼⠲⠻"] # 0x2779 - - "❺": [t: "⠫⠸⠉⠸⠫⠼⠢⠻"] # 0x277a - - "❻": [t: "⠫⠸⠉⠸⠫⠼⠖⠻"] # 0x277b - - "❼": [t: "⠫⠸⠉⠸⠫⠼⠶⠻"] # 0x277c - - "❽": [t: "⠫⠸⠉⠸⠫⠼⠦⠻"] # 0x277d - - "❾": [t: "⠫⠸⠉⠸⠫⠼⠔⠻"] # 0x277e - - "❿": [t: "⠫⠸⠉⠸⠫⠼⠂⠴⠻"] # 0x277f - - "⓫": [t: "⠫⠸⠉⠸⠫⠼⠂⠂⠻"] # 0x24eb - - "⓬": [t: "⠫⠸⠉⠸⠫⠼⠂⠆⠻"] # 0x24ec - - "⓭": [t: "⠫⠸⠉⠸⠫⠼⠂⠒⠻"] # 0x24ed - - "⓮": [t: "⠫⠸⠉⠸⠫⠼⠂⠲⠻"] # 0x24ee - - "⓯": [t: "⠫⠸⠉⠸⠫⠼⠂⠢⠻"] # 0x24ef - - "⓰": [t: "⠫⠸⠉⠸⠫⠼⠂⠖⠻"] # 0x24f0 - - "⓱": [t: "⠫⠸⠉⠸⠫⠼⠂⠶⠻"] # 0x24f1 - - "⓲": [t: "⠫⠸⠉⠸⠫⠼⠂⠦⠻"] # 0x24f2 - - "⓳": [t: "⠫⠸⠉⠸⠫⠼⠂⠔⠻"] # 0x24f3 - - "⓴": [t: "⠫⠸⠉⠸⠫⠼⠆⠴⠻"] # 0x24f4 - - - "🅐": [t: "⠫⠸⠉⠸⠫⠠⠁⠻"] # 0x1f150 - - "🅑": [t: "⠫⠸⠉⠸⠫⠠⠃⠻"] # 0x1f151 - - "🅒": [t: "⠫⠸⠉⠸⠫⠠⠉⠻"] # 0x1f152 - - "🅓": [t: "⠫⠸⠉⠸⠫⠠⠙⠻"] # 0x1f153 - - "🅔": [t: "⠫⠸⠉⠸⠫⠠⠑⠻"] # 0x1f154 - - "🅕": [t: "⠫⠸⠉⠸⠫⠠⠋⠻"] # 0x1f155 - - "🅖": [t: "⠫⠸⠉⠸⠫⠠⠛⠻"] # 0x1f156 - - "🅗": [t: "⠫⠸⠉⠸⠫⠠⠓⠻"] # 0x1f157 - - "🅘": [t: "⠫⠸⠉⠸⠫⠠⠊⠻"] # 0x1f158 - - "🅙": [t: "⠫⠸⠉⠸⠫⠠⠚⠻"] # 0x1f159 - - "🅚": [t: "⠫⠸⠉⠸⠫⠠⠅⠻"] # 0x1f15a - - "🅛": [t: "⠫⠸⠉⠸⠫⠠⠇⠻"] # 0x1f15b - - "🅜": [t: "⠫⠸⠉⠸⠫⠠⠍⠻"] # 0x1f15c - - "🅝": [t: "⠫⠸⠉⠸⠫⠠⠝⠻"] # 0x1f15d - - "🅞": [t: "⠫⠸⠉⠸⠫⠠⠕⠻"] # 0x1f15e - - "🅟": [t: "⠫⠸⠉⠸⠫⠠⠏⠻"] # 0x1f15f - - "🅠": [t: "⠫⠸⠉⠸⠫⠠⠟⠻"] # 0x1f160 - - "🅡": [t: "⠫⠸⠉⠸⠫⠠⠗⠻"] # 0x1f161 - - "🅢": [t: "⠫⠸⠉⠸⠫⠠⠎⠻"] # 0x1f162 - - "🅣": [t: "⠫⠸⠉⠸⠫⠠⠞⠻"] # 0x1f163 - - "🅤": [t: "⠫⠸⠉⠸⠫⠠⠥⠻"] # 0x1f164 - - "🅥": [t: "⠫⠸⠉⠸⠫⠠⠧⠻"] # 0x1f165 - - "🅦": [t: "⠫⠸⠉⠸⠫⠠⠺⠻"] # 0x1f166 - - "🅧": [t: "⠫⠸⠉⠸⠫⠠⠭⠻"] # 0x1f167 - - "🅨": [t: "⠫⠸⠉⠸⠫⠠⠽⠻"] # 0x1f168 - - "🅩": [t: "⠫⠸⠉⠸⠫⠠⠵⠻"] # 0x1f169 - - # --- Nemeth Default Alphabet negative-circled-sans-serif chars. --- - - "🄌": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠴⠻"] # 0x1f10c - - "➊": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠂⠻"] # 0x278a - - "➋": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠆⠻"] # 0x278b - - "➌": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠒⠻"] # 0x278c - - "➍": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠲⠻"] # 0x278d - - "➎": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠢⠻"] # 0x278e - - "➏": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠖⠻"] # 0x278f - - "➐": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠶⠻"] # 0x2790 - - "➑": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠦⠻"] # 0x2791 - - "➒": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠔⠻"] # 0x2792 - - "➓": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠂⠴⠻"] # 0x2793 - - # --- Nemeth Default Alphabet negative-squared chars. --- - - "🅰-🆉": # 0x1f170 - 0x1f189 - - tc: "⠫⠸⠲⠸⠫⠠" - - spell: "translate('.', '🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - tc: "⠻" - - # --- Nemeth Default Alphabet parenthesized chars. --- - - - "⑴-⑼": # 0x2474 - 0x247c - - tc: "⠷" - - spell: "translate('.', '⑴⑵⑶⑷⑸⑹⑺⑻⑼', '123456789')" - - tc: "⠾" - - - "⑽": [t: "⠷⠂⠴⠾"] # 0x247d - - "⑾": [t: "⠷⠂⠂⠾"] # 0x247e - - "⑿": [t: "⠷⠂⠆⠾"] # 0x247f - - "⒀": [t: "⠷⠂⠒⠾"] # 0x2480 - - "⒁": [t: "⠷⠂⠲⠾"] # 0x2481 - - "⒂": [t: "⠷⠂⠢⠾"] # 0x2482 - - "⒃": [t: "⠷⠂⠖⠾"] # 0x2483 - - "⒄": [t: "⠷⠂⠶⠾"] # 0x2484 - - "⒅": [t: "⠷⠂⠦⠾"] # 0x2485 - - "⒆": [t: "⠷⠂⠔⠾"] # 0x2486 - - "⒇": [t: "⠷⠆⠴⠾"] # 0x2487 - - - "🄐-🄩": # 0x1f110 - 0x1f129 - - tc: "⠷⠠" - - spell: "translate('.', '🄐🄑🄒🄓🄔🄕🄖🄗🄘🄙🄚🄛🄜🄝🄞🄟🄠🄡🄢🄣🄤🄥🄦🄧🄨🄩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - tc: "⠾" - - - "⒜-⒵": # 0x249c - 0x24b5 - - tc: "⠷" - - spell: "translate('.', '⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵', 'abcdefghijklmnopqrstuvwxyz')" - - tc: "⠾" - - # --- Nemeth Default Alphabet period chars. --- - - "🄀": [t: "⠴⠸⠲"] # 0x1f100 - - "⒈": [t: "⠂⠸⠲"] # 0x2488 - - "⒉": [t: "⠆⠸⠲"] # 0x2489 - - "⒊": [t: "⠒⠸⠲"] # 0x248a - - "⒋": [t: "⠲⠸⠲"] # 0x248b - - "⒌": [t: "⠢⠸⠲"] # 0x248c - - "⒍": [t: "⠖⠸⠲"] # 0x248d - - "⒎": [t: "⠶⠸⠲"] # 0x248e - - "⒏": [t: "⠦⠸⠲"] # 0x248f - - "⒐": [t: "⠔⠸⠲"] # 0x2490 - - - "⒑": [t: "⠂⠴⠸⠲"] # 0x2491 - - "⒒": [t: "⠂⠂⠸⠲"] # 0x2492 - - "⒓": [t: "⠂⠆⠸⠲"] # 0x2493 - - "⒔": [t: "⠂⠒⠸⠲"] # 0x2494 - - "⒕": [t: "⠂⠲⠸⠲"] # 0x2495 - - "⒖": [t: "⠂⠢⠸⠲"] # 0x2496 - - "⒗": [t: "⠂⠖⠸⠲"] # 0x2497 - - "⒘": [t: "⠂⠶⠸⠲"] # 0x2498 - - "⒙": [t: "⠂⠦⠸⠲"] # 0x2499 - - "⒚": [t: "⠂⠔⠸⠲"] # 0x249a - - "⒛": [t: "⠆⠴⠸⠲"] # 0x249b - - # --- Nemeth Default Alphabet sans-serif chars. --- - - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 - - tc: "SE" - - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝖺-𝗓": # 0x1d5ba - 0x1d5d3 - - tc: "SE" - - spell: "translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝟢-𝟫": # 0x1d7e2 - 0x1d7eb - - tc: "S" - - spell: "translate('.', '𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫', '0123456789')" - - # --- Nemeth Default Alphabet sans-serif-bold chars. --- - - "𝗔-𝗭": # 0x1d5d4 - 0x1d5ed - - tc: "SBE" - - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝗮-𝘇": # 0x1d5ee - 0x1d607 - - tc: "SBE" - - spell: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝝖-𝝮": # 0x1d756 - 0x1d76e - - tc: "SBG" - - spell: "translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭𝝮', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝝰-𝞀": # 0x1d770 - 0x1d780 - - tc: "SBG" - - spell: "translate('.', '𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿𝞀', 'αβγδεζηθικλμνξοπρ')" - - - "𝝯": [tc: "SBGL⠫"] # 0x1d76f - - - "𝞁": [tc: "SBVL⠎"] # 0x1d781 - - - "𝞂-𝞄": # 0x1d782 - 0x1d784 - - tc: "SBG" - - spell: "translate('.', '𝞂𝞃𝞄', 'στυ')" - - - "𝞅": [tc: "SBVL⠋"] # 0x1d785 - - - "𝞆-𝞈": # 0x1d786 - 0x1d788 - - tc: "SBG" - - spell: "translate('.', '𝞆𝞇𝞈', 'χψω')" - - - "𝞉": [tc: "⠠⠨⠸⠈⠙"] # 0x1d789 - - "𝞊": [tc: "SBGL⠑"] # 0x1d78a - - "𝞋": [tc: "SBVL⠹"] # 0x1d78b - - - "𝞌-𝞏": # 0x1d78c - 0x1d78f - - tc: "SBG" - - spell: "translate('.', '𝞌𝞍𝞎𝞏', 'κφρπ')" - - - "𝟬-𝟵": # 0x1d7ec - 0x1d7f5 - - tc: "SB" - - spell: "translate('.', '𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵', '0123456789')" - - # --- Nemeth Default Alphabet sans-serif-bold-italic chars. --- - - - "𝘼-𝙕": # 0x1d63c - 0x1d655 - - tc: "SBE" - - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝙖-𝙯": # 0x1d656 - 0x1d66f - - tc: "SBE" - - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" - - - "𝞐-𝞨": # 0x1d790 - 0x1d7a8 - - tc: "SBG" - - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" - - - "𝞩": [t: "SBGL⠫"] # 0x1d7a9 - - - "𝞪-𝞺": # 0x1d7aa - 0x1d7ba - - tc: "SBG" - - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺', 'αβγδεζηθικλμνξοπρ')" - - - "𝞻": [t: "SBVL⠎"] # 0x1d7bb - - "𝞼": [t: "SBGL⠎"] # 0x1d7bc - - "𝞽": [t: "SBGL⠞"] # 0x1d7bd - - "𝞾": [t: "SBGL⠥"] # 0x1d7be - - "𝞿": [t: "SBVL⠋"] # 0x1d7bf - - - "𝟀": [t: "SBGL⠯"] # 0x1d7c0 - - "𝟁": [t: "SBGL⠽"] # 0x1d7c1 - - "𝟂": [t: "SBGL⠺"] # 0x1d7c2 - - "𝟃": [t: "SBVL⠙"] # 0x1d7c3 - - - "𝟄": [t: "SBGL⠑"] # 0x1d7c4 - - "𝟅": [t: "SBVL⠹"] # 0x1d7c5 - - - "𝟆": [t: "SBGL⠅"] # 0x1d7c6 - - "𝟇": [t: "SBGL⠋"] # 0x1d7c7 - - "𝟈": [t: "SBGL⠗"] # 0x1d7c8 - - "𝟉": [t: "SBGL⠏"] # 0x1d7c9 - - # --- Nemeth Default Alphabet sans-serif-italic chars. --- - - "𝘈-𝘡": # 0x1d608 - 0x1d621 - - tc: "SE" - - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - - - "𝘢-𝘻": # 0x1d622 - 0x1d63b - - tc: "SE" - - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" - - # --- Nemeth Default Alphabet script chars. --- - - "𝒜-𝒵": # 0x1d49c - 0x1d4b5 - - tc: "TE" - - spell: "translate('.', '𝒜𝒞𝒟𝒢𝒥𝒦𝒩𝒪𝒫𝒬𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'ACDGJKNOPQSTUVWXYZ')" - - - "ℬ": [t: "TECL⠃"] # 0x212c - - "ℰ": [t: "TECL⠑"] # 0x2130 - - "ℱ": [t: "TECL⠋"] # 0x2131 - - "ℋ": [t: "TECL⠓"] # 0x210b - - "ℐ": [t: "TECL⠊"] # 0x2110 - - "ℒ": [t: "TECL⠇"] # 0x2112 - - "ℳ": [t: "TECL⠍"] # 0x2133 - - "ℛ": [t: "TECL⠗"] # 0x211b - - - "𝒶-𝓏": # 0x1d4b6 - 0x1d4cf - - tc: "TE" - - spell: "translate('.', '𝒶𝒷𝒸𝒹𝒻𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏', 'abcdfhijklmnpqrstuvwxyz')" - - - "ℯ": [t: "TEL⠑"] # 0x212f - - "ℊ": [t: "TEL⠛"] # 0x210a - - "ℴ": [t: "TEL⠕"] # 0x2134 - - # --- Nemeth Default Alphabet squared chars. --- - - - "🄰": [t: "⠫⠲⠸⠫⠠⠁⠻"] # 0x1f130 - - "🄱": [t: "⠫⠲⠸⠫⠠⠃⠻"] # 0x1f131 - - "🄲": [t: "⠫⠲⠸⠫⠠⠉⠻"] # 0x1f132 - - "🄳": [t: "⠫⠲⠸⠫⠠⠙⠻"] # 0x1f133 - - "🄴": [t: "⠫⠲⠸⠫⠠⠑⠻"] # 0x1f134 - - "🄵": [t: "⠫⠲⠸⠫⠠⠋⠻"] # 0x1f135 - - "🄶": [t: "⠫⠲⠸⠫⠠⠛⠻"] # 0x1f136 - - "🄷": [t: "⠫⠲⠸⠫⠠⠓⠻"] # 0x1f137 - - "🄸": [t: "⠫⠲⠸⠫⠠⠊⠻"] # 0x1f138 - - "🄹": [t: "⠫⠲⠸⠫⠠⠚⠻"] # 0x1f139 - - "🄺": [t: "⠫⠲⠸⠫⠠⠅⠻"] # 0x1f13a - - "🄻": [t: "⠫⠲⠸⠫⠠⠇⠻"] # 0x1f13b - - "🄼": [t: "⠫⠲⠸⠫⠠⠍⠻"] # 0x1f13c - - "🄽": [t: "⠫⠲⠸⠫⠠⠝⠻"] # 0x1f13d - - "🄾": [t: "⠫⠲⠸⠫⠠⠕⠻"] # 0x1f13e - - "🄿": [t: "⠫⠲⠸⠫⠠⠏⠻"] # 0x1f13f - - "🅀": [t: "⠫⠲⠸⠫⠠⠟⠻"] # 0x1f140 - - "🅁": [t: "⠫⠲⠸⠫⠠⠗⠻"] # 0x1f141 - - "🅂": [t: "⠫⠲⠸⠫⠠⠎⠻"] # 0x1f142 - - "🅃": [t: "⠫⠲⠸⠫⠠⠞⠻"] # 0x1f143 - - "🅄": [t: "⠫⠲⠸⠫⠠⠥⠻"] # 0x1f144 - - "🅅": [t: "⠫⠲⠸⠫⠠⠧⠻"] # 0x1f145 - - "🅆": [t: "⠫⠲⠸⠫⠠⠺⠻"] # 0x1f146 - - "🅇": [t: "⠫⠲⠸⠫⠠⠭⠻"] # 0x1f147 - - "🅈": [t: "⠫⠲⠸⠫⠠⠽⠻"] # 0x1f148 - - "🅉": [t: "⠫⠲⠸⠫⠠⠵⠻"] # 0x1f149 - - # --- Nemeth Default Alphabet sub chars. --- - - - "₀": [t: "⠰⠴"] # 0x2080 - - "₁": [t: "⠰⠂"] # 0x2081 - - "₂": [t: "⠰⠆"] # 0x2082 - - "₃": [t: "⠰⠒"] # 0x2083 - - "₄": [t: "⠰⠲"] # 0x2084 - - "₅": [t: "⠰⠢"] # 0x2085 - - "₆": [t: "⠰⠖"] # 0x2086 - - "₇": [t: "⠰⠶"] # 0x2087 - - "₈": [t: "⠰⠦"] # 0x2088 - - "₉": [t: "⠰⠔"] # 0x2089 - - # --- Nemeth Default Alphabet super chars. --- - - - "⁰": [t: "⠘⠴"] # 0x2070 - - "¹": [t: "⠘⠂"] # 0xb9 - - "²": [t: "⠘⠆"] # 0xb2 - - "³": [t: "⠘⠒"] # 0xb3 - - "⁴": [t: "⠘⠲"] # 0x2074 - - "⁵": [t: "⠘⠢"] # 0x2075 - - "⁶": [t: "⠘⠖"] # 0x2076 - - "⁷": [t: "⠘⠶"] # 0x2077 - - "⁸": [t: "⠘⠦"] # 0x2078 - - "⁹": [t: "⠘⠔"] # 0x2079 - - # --- Nemeth Default Characters latin-lower-phonetic chars. --- - - - "ø": [t: "⠈⠕"] # 0xf8 - - # --- Nemeth Default Characters math_geometry chars. --- - - - "─": [t: "⠱"] # 0x2500 - - "│": [t: "⠳"] # 0x2502 - - "▰": [t: "⠫⠸⠛"] # 0x25b0 - - "▱": [t: "⠫⠛"] # 0x25b1 - - "▽": [t: "⠨⠫"] # 0x25bd - - "◆": [t: "⠫⠸⠙"] # 0x25c6 - - "◇": [t: "⠫⠙"] # 0x25c7 - - "◈": [t: "⠫⠸⠙"] # 0x25c8 - - "◉": [t: "⠫⠸⠉"] # 0x25c9 - - "◊": [t: "⠫⠙"] # 0x25ca - - "⬟": [t: "⠫⠸⠢"] # 0x2b1f - - "⬠": [t: "⠫⠢"] # 0x2b20 - - "⬡": [t: "⠫⠖"] # 0x2b21 - - "⬢": [t: "⠫⠸⠖"] # 0x2b22 - - "⬣": [t: "⠫⠸⠖"] # 0x2b23 - - "⬤": [t: "⠫⠸⠉"] # 0x2b24 - - "⬥": [t: "⠫⠸⠙"] # 0x2b25 - - # --- Nemeth Default Characters math_symbols chars. --- - - - "\"": [t: "⠄⠄"] # 0x22 - - "¡": [t: "⠖"] # 0xa1 - - "¢": [t: "⠈⠉"] # 0xa2 - - "£": [t: "⠈⠇"] # 0xa3 - - "¤": [t: "⠈⠫"] # 0xa4 - - "¥": [t: "⠈⠽"] # 0xa5 - - "§": [t: "⠈⠠⠎"] # 0xa7 - - "©": [t: "⠘⠉"] # 0xa9 (Copyright) - - "ª": [t: "⠸⠰⠁"] # 0xaa - - "«": [t: "⠐⠅⠈⠐⠅⠻"] # 0xab - - "®": [t: "⠘⠗"] # 0xae - - "´": [t: "⠈"] # 0xb4 - - "µ": [t: "⠨⠍"] # 0xb5 - - "¶": [t: "⠈⠠⠏"] # 0xb6 - - "¿": [t: "⠦"] # 0xbf - - "ʹ": [t: "⠄"] # 0x2b9 - - "ʺ": [t: "⠄⠄"] # 0x2ba - - "˙": [t: "⠡"] # 0x2d9 - - "˜": [t: "⠈⠱"] # 0x2dc - - "‐": [t: "⠤"] # 0x2010 - - "‑": [t: "⠤"] # 0x2011 - - "‒": [t: "⠤⠤"] # 0x2012 - - "–": [t: "⠤⠤"] # 0x2013 - - "‖": [t: "⠳⠳"] # 0x2016 - - "†": [t: "⠸⠻"] # 0x2020 - - "‡": [t: "⠸⠸⠻"] # 0x2021 - - "•": [t: "⠡"] # 0x2022 - - "․": [t: "⠄"] # 0x2024 - - "‥": [t: "⠄⠄"] # 0x2025 - - "′": [t: "⠄"] # 0x2032 - - "″": [t: "⠄⠄"] # 0x2033 - - "‴": [t: "⠄⠄."] # 0x2034 (Triple prime) - - "⁗": [t: "⠄⠄.."] # 0x2057 (Quadruple prime) - - "‼": [t: "⠖⠖"] # 0x203c - - "‾": [t: "⠱"] # 0x203e - - "⁇": [t: "⠹⠹"] # 0x2047 - - "⁈": [t: "⠹⠖"] # 0x2048 - - "⁉": [t: "⠖⠹"] # 0x2049 - - "∀": [t: "⠈⠯"] # 0x2200 - - "∃": [t: "⠈⠿"] # 0x2203 - - "∄": [t: "⠌⠈⠿"] # 0x2204 - - "∅": [t: "⠸⠴"] # 0x2205 - - "∈": [t: "⠈⠑"] # 0x2208 - - "∉": [t: "⠌⠈⠑"] # 0x2209 - - "∊": [t: "⠈⠑"] # 0x220a - - "∋": [t: "⠈⠢"] # 0x220b - - "∌": [t: "⠌⠈⠢"] # 0x220c - - "∍": [t: "⠈⠢"] # 0x220d - - "∎": [t: "⠸⠳"] # 0x220e - - "−": [t: "⠤"] # 0x2212 - - "∓": [t: "⠤⠬"] # 0x2213 - - "∕": [t: "⠸⠌"] # 0x2215 - - "∖": [t: "⠸⠡"] # 0x2216 - - "∗": [t: "⠈⠼"] # 0x2217 - - "∘": [t: "⠨⠡"] # 0x2218 - - "∝": [t: "⠸⠿"] # 0x221d - - "∞": [t: "⠠⠿"] # 0x221e - - "∟": [t: "⠫⠪⠨⠗⠻"] # 0x221f - - "∠": [t: "⠫⠪"] # 0x2220 - - "∢": [t: "⠫⠪⠸⠫⠫⠁⠻"] # 0x2222 - - "∣": [t: "⠳"] # 0x2223 - - "∤": [t: "⠌⠳"] # 0x2224 - - "∥": [t: "⠫⠇"] # 0x2225 - - "∦": [t: "⠌⠫⠇"] # 0x2226 - - "∧": [t: "⠈⠩"] # 0x2227 - - "∨": [t: "⠈⠬"] # 0x2228 - - "∩": [t: "⠨⠩"] # 0x2229 - - "∪": [t: "⠨⠬"] # 0x222a - - "∫": [t: "⠮"] # 0x222b - - "∬": [t: "⠮⠮"] # 0x222c - - "∭": [t: "⠮⠮⠮"] # 0x222d - - "∮": [t: "⠮⠈⠫⠉⠻"] # 0x222e - - "∲": [t: "⠮⠈⠫⠪⠢⠔⠻"] # 0x2232 - - "∳": [t: "⠮⠈⠫⠢⠔⠕⠻"] # 0x2233 - - "∴": [t: "⠠⠡"] # 0x2234 - - "∵": [t: "⠈⠌"] # 0x2235 - - "∶": [t: "⠐⠂"] # 0x2236 - - "∷": [t: "⠰⠆"] # 0x2237 - - "∸": [t: "⠨⠤"] # 0x2238 - - "∼": [t: "⠈⠱"] # 0x223c - - "≁": [t: "⠌⠈⠱"] # 0x2241 - - "≂": [t: "⠱⠈⠱"] # 0x2242 - - "≃": [t: "⠈⠱⠱"] # 0x2243 - - "≄": [t: "⠌⠈⠱⠱"] # 0x2244 - - "≅": [t: "⠈⠱⠨⠅"] # 0x2245 - - "≆": [t: "⠈⠱⠌⠨⠅"] # 0x2246 - - "≇": [t: "⠌⠈⠱⠨⠅"] # 0x2247 - - "≈": [t: "⠈⠱⠈⠱"] # 0x2248 - - "≉": [t: "⠌⠈⠱⠈⠱"] # 0x2249 - - "≊": [t: "⠈⠱⠈⠱⠱"] # 0x224a - - "≍": [t: "⠈⠣⠠⠣"] # 0x224d - - "≎": [t: "⠈⠣⠠⠣"] # 0x224e - - "≐": [t: "⠐⠨⠅⠣⠡⠻"] # 0x2250 - - "≑": [t: "⠐⠨⠅⠩⠡⠣⠡⠻"] # 0x2251 - - "≗": [t: "⠐⠨⠅⠣⠨⠡⠻"] # 0x2257 - - "≘": [t: "⠐⠨⠅⠣⠫⠁⠻"] # 0x2258 - - "≙": [t: "⠐⠨⠅⠣⠸⠣⠻"] # 0x2259 - - "≚": [t: "⠐⠨⠅⠣⠸⠩⠻"] # 0x225a - - "≛": [t: "⠐⠨⠅⠣⠫⠸⠎⠻"] # 0x225b - - "≜": [t: "⠐⠨⠅⠣⠫⠞⠻"] # 0x225c - - "≝": [t: "⠐⠨⠅⠣⠙⠑⠋⠻"] # 0x225d - - "≞": [t: "⠐⠨⠅⠣⠍⠻"] # 0x225e - - "≟": [t: "⠐⠨⠅⠣⠸⠦⠻"] # 0x225f - - "≠": [t: "⠌⠨⠅"] # 0x2260 - - "≢": [t: "⠌⠸⠇"] # 0x2262 - - "≤": [t: "⠐⠅⠱"] # 0x2264 - - "≥": [t: "⠨⠂⠱"] # 0x2265 - - "≦": [t: "⠐⠅⠨⠅"] # 0x2266 - - "≧": [t: "⠨⠂⠨⠅"] # 0x2267 - - "≨": [t: "⠐⠅⠌⠨⠅"] # 0x2268 - - "≩": [t: "⠨⠂⠌⠨⠅"] # 0x2269 - - "≪": [t: "⠐⠅⠈⠐⠅⠻"] # 0x226a - - "≫": [t: "⠨⠂⠈⠨⠂⠻"] # 0x226b - - "≭": [t: "⠌⠈⠣⠠⠣"] # 0x226d - - "≮": [t: "⠌⠐⠅"] # 0x226e - - "≯": [t: "⠌⠨⠂"] # 0x226f - - "≰": [t: "⠌⠐⠅⠱"] # 0x2270 - - "≱": [t: "⠌⠨⠂⠱"] # 0x2271 - - "≲": [t: "⠐⠅⠈⠱"] # 0x2272 - - "≳": [t: "⠨⠂⠈⠱"] # 0x2273 - - "≴": [t: "⠌⠐⠅⠈⠱"] # 0x2274 - - "≵": [t: "⠌⠨⠂⠈⠱"] # 0x2275 - - "≶": [t: "⠐⠅⠨⠂"] # 0x2276 - - "≷": [t: "⠨⠂⠐⠅"] # 0x2277 - - "≸": [t: "⠌⠐⠅⠨⠂"] # 0x2278 - - "≹": [t: "⠌⠨⠂⠐⠅"] # 0x2279 - - "≺": [t: "⠨⠐⠅"] # 0x227a - - "≻": [t: "⠨⠨⠂"] # 0x227b - - "≼": [t: "⠨⠐⠅⠱"] # 0x227c - - "≽": [t: "⠨⠨⠐⠱"] # 0x227d - - "≾": [t: "⠨⠐⠅⠈⠱"] # 0x227e - - "≿": [t: "⠨⠨⠂⠈⠱"] # 0x227f - - "⊀": [t: "⠌⠨⠐⠅"] # 0x2280 - - "⊁": [t: "⠌⠨⠐⠅"] # 0x2281 - - "⊂": [t: "⠸⠐⠅"] # 0x2282 - - "⊃": [t: "⠸⠨⠂"] # 0x2283 - - "⊄": [t: "⠌⠸⠐⠅"] # 0x2284 - - "⊅": [t: "⠌⠸⠨⠂"] # 0x2285 - - "⊆": [t: "⠸⠐⠅⠱"] # 0x2286 - - "⊇": [t: "⠸⠨⠂⠱"] # 0x2287 - - "⊈": [t: "⠌⠸⠐⠅⠱"] # 0x2288 - - "⊉": [t: "⠌⠸⠨⠂⠱"] # 0x2289 - - "⊊": [t: "⠸⠐⠅⠌⠨⠅"] # 0x228a - - "⊋": [t: "⠸⠨⠂⠌⠨⠅"] # 0x228b - - "⊥": [t: "⠫⠏"] # 0x22a5 - - "⋀": [t: "⠈⠩"] # 0x22c0 - - "⋁": [t: "⠈⠬"] # 0x22c1 - - "⋂": [t: "⠨⠩"] # 0x22c2 - - "⋃": [t: "⠨⠬"] # 0x22c3 - - "⋄": [t: "⠫⠙"] # 0x22c4 - - "⋜": [t: "⠱⠐⠅"] # 0x22dc - - "⋝": [t: "⠱⠨⠂"] # 0x22dd - - "⟂": [t: "⠫⠏"] # 0x27c2 - - "⦀": [t: "⠳⠳⠳"] # 0x2980 - - "⦶": [t: "⠫⠉⠸⠫⠳⠻"] # 0x29b6 - - "⦷": [t: "⠫⠉⠸⠫⠫⠇⠻"] # 0x29b7 - - "⦸": [t: "⠫⠉⠸⠫⠸⠡⠻"] # 0x29b8 - - "⦹": [t: "⠫⠉⠸⠫⠫⠏⠻"] # 0x29b9 - - "⦿": [t: "⠫⠉⠸⠫⠔⠔⠻"] # 0x29bf - - "⧀": [t: "⠫⠉⠸⠫⠐⠅⠻"] # 0x29c0 - - "⧁": [t: "⠫⠉⠸⠫⠨⠂⠻"] # 0x29c1 - - "⨀": [t: "⠫⠉⠸⠫⠡⠻"] # 0x2a00 - - "⨁": [t: "⠫⠉⠸⠫⠬⠻"] # 0x2a01 - - "⨂": [t: "⠫⠉⠸⠫⠈⠡⠻"] # 0x2a02 - - "⪯": [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: "⠈⠎"] # 0xfe69 - - "$": [t: "⠈⠎"] # 0xff04 - - "_": [t: "⠱"] # 0xff3f - - "~": [t: "⠈⠱"] # 0xff5e - - "¢": [t: "⠈⠉"] # 0xffe0 - - "£": [t: "⠈⠇"] # 0xffe1 - - " ̄": [t: "⠱"] # 0xffe3 - - "¥": [t: "⠈⠽"] # 0xffe5 - - # --- Nemeth Default Characters rest chars. --- - - - "ℵ": [t: "⠠⠠⠁"] # 0x2135 - - "ℶ": [t: "⠠⠠⠃"] # 0x2136 - - "ℷ": [t: "⠠⠠⠉"] # 0x2137 - - "ℸ": [t: "⠠⠠⠙"] # 0x2138 - - "à": [t: "⠈⠁"] # 0xe0 - - "á": [t: "⠈⠁"] # 0xe1 - - "â": [t: "⠈⠁"] # 0xe2 - - "ã": [t: "⠈⠁"] # 0xe3 - - "ä": [t: "⠈⠁"] # 0xe4 - - "å": [t: "⠈⠁"] # 0xe5 - - "ç": [t: "⠈⠉"] # 0xe7 - - "è": [t: "⠈⠑"] # 0xe8 - - "é": [t: "⠈⠑"] # 0xe9 - - "ê": [t: "⠈⠑"] # 0xea - - "ë": [t: "⠈⠑"] # 0xeb - - "ì": [t: "⠈⠊"] # 0xec - - "í": [t: "⠈⠊"] # 0xed - - "î": [t: "⠈⠊"] # 0xee - - "ï": [t: "⠈⠊"] # 0xef - - "ñ": [t: "⠈⠝"] # 0xf1 - - "ò": [t: "⠈⠕"] # 0xf2 - - "ó": [t: "⠈⠕"] # 0xf3 - - "ô": [t: "⠈⠕"] # 0xf4 - - "õ": [t: "⠈⠕"] # 0xf5 - - "ö": [t: "⠈⠕"] # 0xf6 - - "ù": [t: "⠈⠥"] # 0xf9 - - "ú": [t: "⠈⠥"] # 0xfa - - "û": [t: "⠈⠥"] # 0xfb - - "ü": [t: "⠈⠥"] # 0xfc - - "ý": [t: "⠈⠽"] # 0xfd - - "ÿ": [t: "⠈⠽"] # 0xff - - - "À": [t: "⠠⠈⠁"] # 0xc0 - - "Á": [t: "⠠⠈⠁"] # 0xc1 - - "Â": [t: "⠠⠈⠁"] # 0xc2 - - "Ã": [t: "⠠⠈⠁"] # 0xc3 - - "Ä": [t: "⠠⠈⠁"] # 0xc4 - - "Å": [t: "⠠⠈⠁"] # 0xc5 - - "Ç": [t: "⠠⠈⠉"] # 0xc7 - - "È": [t: "⠠⠈⠑"] # 0xc8 - - "É": [t: "⠠⠈⠑"] # 0xc9 - - "Ê": [t: "⠠⠈⠑"] # 0xca - - "Ë": [t: "⠠⠈⠑"] # 0xcb - - "Ì": [t: "⠠⠈⠊"] # 0xcc - - "Í": [t: "⠠⠈⠊"] # 0xcd - - "Î": [t: "⠠⠈⠊"] # 0xce - - "Ï": [t: "⠠⠈⠊"] # 0xcf - - "Ñ": [t: "⠠⠈⠝"] # 0xd1 - - "Ò": [t: "⠠⠈⠕"] # 0xd2 - - "Ó": [t: "⠠⠈⠕"] # 0xd3 - - "Ô": [t: "⠠⠈⠕"] # 0xd4 - - "Ö": [t: "⠠⠈⠕"] # 0xd6 - - "Ù": [t: "⠠⠈⠥"] # 0xd9 - - "Ú": [t: "⠠⠈⠥"] # 0xda - - "Û": [t: "⠠⠈⠥"] # 0xdb - - "Ü": [t: "⠠⠈⠥"] # 0xdc - - "Ý": [t: "⠠⠈⠽"] # 0xdd - - "←": [t: "⠫⠪⠒⠒"] # 0x2190 - - - "↑": # 0x2191 - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠣"] - else: [t: "⠫⠣⠒⠒⠕"] - - - "↓": # 0x2193 - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠩"] - else: [t: "⠫⠩⠒⠒⠕"] - - - "↔": [t: "⠫⠪⠒⠒⠕"] # 0x2194 - - "↕": [t: "⠫⠣⠩⠪⠒⠒⠕"] # 0x2195 - - "↖": [t: "⠫⠘⠪⠒⠒"] # 0x2196 - - "↗": [t: "⠫⠘⠒⠒⠕"] # 0x2197 - - "↘": [t: "⠫⠰⠒⠒⠕"] # 0x2198 - - "↙": [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: "⠫⠠⠳⠒⠒⠕"] # 0x21b4 - - "↵": [t: "⠫⠩⠠⠳⠒⠒⠕"] # 0x21b5 - - "↶": [t: "⠫⠢⠔⠕"] # 0x21b6 - - "↷": [t: "⠫⠪⠢⠔"] # 0x21b7 - - "↺": [t: "⠫⠢⠔⠕"] # 0x21ba - - "↻": [t: "⠫⠪⠢⠔"] # 0x21bb - - "⇄": [t: "⠫⠒⠒⠕⠫⠪⠒⠒"] # 0x21c4 - - "⇅": [t: "⠫⠣⠒⠒⠕⠐⠫⠩⠒⠒⠕"] # 0x21c5 - - "⇆": [t: "⠫⠪⠒⠒⠫⠒⠒⠕"] # 0x21c6 - - "⇇": [t: "⠫⠚⠒⠒⠫⠚⠒⠒"] # 0x21c7 - - "⇈": [t: "⠫⠣⠒⠒⠕⠐⠫⠣⠒⠒⠕"] # 0x21c8 - - "⇉": [t: "⠫⠒⠒⠕⠫⠒⠒⠕"] # 0x21c9 - - "⇊": [t: "⠫⠩⠒⠒⠕⠐⠫⠩⠒⠒⠕"] # 0x21ca - - "⇍": [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 FIX: how to represent triple shafts? - - "⇛": [t: "⠫⠶⠶⠕⠕"] # 0x21db FIX: how to represent triple shafts? - - "⇜": [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: "⠫⠣⠸⠪⠒⠒⠕"] # 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: "⠙"] # 0x2146 - - "ⅇ": [t: "⠑"] # 0x2147 - - "ⅈ": [t: "⠊"] # 0x2148 - - "⌜": [t: "⠈⠘⠷"] # 0x231c - - "⌝": [t: "⠈⠘⠾"] # 0x231d - - "⌞": [t: "⠈⠰⠷"] # 0x231e - - "⌟": [t: "⠈⠰⠾"] # 0x231f - - "〈": [t: "⠨⠨⠷"] # 0x2329 - - "〉": [t: "⠨⠨⠾"] # 0x232a - - "⏞": [t: "⠨⠷"] # 0x23de - - "⏟": [t: "⠨⠾"] # 0x23df - - "⎴": [t: "⠈⠷"] # 0x23b4 - - "⎵": [t: "⠈⠾"] # 0x23b5 - - "⟪": [t: "⠨⠨⠨⠷"] # 0x27ea - - "⟫": [t: "⠨⠨⠨⠾"] # 0x27eb - - "〈": [t: "⠨⠨⠷"] # 0x3008 - - "〉": [t: "⠨⠨⠾"] # 0x3009 - - "《": [t: "⠨⠨⠨⠷"] # 0x300a - - "》": [t: "⠨⠨⠨⠾"] # 0x300b - - "「": [t: "⠈⠘⠷"] # 0x300c - - "」": [t: "⠈⠘⠾"] # 0x300d - - "↼": [t: "⠫⠈⠪⠒⠒"] # 0x21bc - - "↽": [t: "⠫⠠⠪⠒⠒"] # 0x21bd - - "↾": [t: "⠫⠣⠒⠒⠠⠕"] # 0x21be - - "↿": [t: "⠫⠣⠒⠒⠈⠕"] # 0x21bf - - "⇀": [t: "⠫⠒⠒⠈⠕"] # 0x21c0 - - "⇁": [t: "⠫⠒⠒⠠⠕"] # 0x21c1 - - "⇂": [t: "⠫⠪⠒⠒⠈⠕"] # 0x21c2 - - "⇃": [t: "⠫⠩⠒⠒⠠⠕"] # 0x21c3 - - "⇋": # 0x21cb - - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠪⠒⠫⠒⠕"] - else: [t: "⠫⠈⠪⠒⠒⠫⠒⠒⠈⠕"] - - "⇌": # 0x21cc - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] - - - "℔": [t: "⠳"] # 0x2114 - - "™": [t: "⠘⠞"] # 0x2122 - - "Å": [t: "⠈⠠⠁"] # 0x212b - - "­": [t: "⠤"] # 0xad - - " -​": [t: "W"] # 0x2000 - 0x200b (various spaces / ZWSP) - - "
": [t: "W"] # 0x2028 - - "
": [t: "W"] # 0x2029 - - " ": [t: "W"] # 0x202f - - " ": [t: "W"] # 0x205f - - "₣": [t: "⠈⠋"] # 0x20A3 (French franc sign - - "₦": [t: "⠈⠝"] # 0x2046 (Naira sign) - - "₩": [t: "⠈⠺"] # 0x20A9 (Won sign) - - "€": [t: "⠈⠑"] # 0x20AC (Euro sign) - - "": [t: "W"] # 0xfeff - - "☆": [t: "⠫⠎"] # 0x2606 - - "✕": [t: "⠈⠡"] # 0x2715 - - "¼": [t: "⠹⠂⠌⠲⠼"] # 0xbc - - "½": [t: "⠹⠂⠌⠆⠼"] # 0xbd - - "¾": [t: "⠹⠒⠌⠲⠼"] # 0xbe - - "⅐": [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: "⠹⠴⠌⠒⠼"] # 0x2189 - - "㉈": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠂⠴⠻⠻"] # 0x3248 - - "㉉": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠆⠴⠻⠻"] # 0x3249 - - "㉊": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠒⠴⠻⠻"] # 0x324a - - "㉋": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠲⠴⠻⠻"] # 0x324b - - "㉌": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠢⠴⠻⠻"] # 0x324c - - "㉍": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠖⠴⠻⠻"] # 0x324d - - "㉎": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠶⠴⠻⠻"] # 0x324e - - "㉏": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠦⠴⠻⠻"] # 0x324f - - - - "‘": [t: "P⠠⠦"] # 0x2018 (Left single quotation mark) - - "’": [t: "P⠠⠴"] # 0x2019 (Right single quotation mark) - - "“": [t: "P⠦"] # 0x201C (Left double quotation mark) - - "”": [t: "P⠴"] # 0x201D (Right double quotation mark) - - "‰": [t: "`00"] - - "ℏ": [t: "⠈⠓"] # 0x210F (Planck constant over two pi) - - "ℓ": [t: "TE⠇"] # 0x2113 (Script small l (differs from 1d4c1: 4-56-123)) - - "℘": [tc: "TCL⠏"] # 0x2118 (Script Capital P) -# - "ℝ": [t: ""] # 0x211D (Double-struck capital R (no nemeth double struck)) -# - "ⅆ": [t: ""] # 0x2146 (Double-struck italic small d) -# - "ⅇ": [t: ""] # 0x2147 (Double-struck italic small e) -# - "⇚": [t: "⠀⠫⠪⠸⠸⠀"] # 0x21DA (Leftwards triple arrow) -# - "⇛": [t: "⠀⠫⠸⠸⠕⠀"] # 0x21DB (Rightwards triple arrow) - - "∔": [t: "⠐⠬⠣⠡⠻"] # 0x2214 (Dot plus) - - "√": [t: "⠜"] # 0x221A (Square root) - - "∛": [t: "⠣⠒⠜"] # 0x221B (Cube root) - - "∜": [t: "⠣⠲⠜"] # 0x221C (Fourth root) - - "∡": [t: "⠫⠪⠸⠫⠫⠁⠻"] # 0x2221 (Measured angle) - - "∹": [t: "⠤⠐⠂"] # 0x2239 (Excess) - - "∺": [t: "⠐⠤⠩⠡⠡⠣⠡⠡⠻"] # 0x223A (Geometric proportion) - - "∻": [t: "⠐⠈⠱⠩⠡⠣⠡⠻"] # 0x223B (Homothetic) - - "∽": [t: "⠠⠱"] # 0x223D (Reversed tilde) - - "≋": [t: "⠈⠱⠈⠱⠈⠱"] # 0x224B (Triple tilde) - - "≌": [t: "⠠⠱⠨⠅"] # 0x224C (All equal to) - - "≏": [t: "⠈⠣⠱"] # 0x224F (Difference between) - - "≔": [t: "⠐⠂⠨⠅"] # 0x2254 (Colon equals) - - "≕": [t: "⠨⠅⠐⠂"] # 0x2255 (Equals colon) - - "≖": [t: "⠀⠨⠡⠈⠨⠅⠻⠀"] # 0x2256 (Ring in equal to) - - "⊌": [t: "⠨⠬⠈⠫⠪⠒⠻"] # 0x228C (Multiset) - - "⊍": [t: "⠡⠈⠨⠬⠻"] # 0x228D (Multiset multiplication) - - "⊎": [t: "⠬⠈⠨⠬⠻"] # 0x228E (Multiset union) - - "⊕": [t: "⠫⠉⠸⠫⠬⠻"] # 0x2295 (Circled plus) - - "⊖": [t: "⠫⠉⠸⠫⠤⠻"] # 0x2296 (Circled minus) - - "⊗": [t: "⠫⠉⠸⠫⠈⠡⠻"] # 0x2297 (Circled times) - - "⊘": [t: "⠫⠉⠸⠫⠸⠌⠻"] # 0x2298 (Circled division slash) - - "⊙": [t: "⠫⠉⠸⠫⠡⠻"] # 0x2299 (Circled dot operator) - - "⊚": [t: "⠫⠉⠸⠫⠘⠨⠡⠻"] # 0x229a (Circled ring operator) - - "⊛": [t: "⠫⠉⠸⠫⠈⠼⠻"] # 0x229B (Circled asterisk operator) - - "⊜": [t: "⠫⠉⠸⠫⠨⠅⠻"] # 0x229C (Circled equals) - - "⊝": [t: "⠫⠉⠸⠫⠤⠤⠻"] # 0x229D (Circled dash) - - "⊞": [t: "⠫⠲⠸⠫⠬⠻"] # 0x229E (Squared plus) - - "⊟": [t: "⠫⠲⠸⠫⠤⠻"] # 0x229F (Squared minus) - - "⊠": [t: "⠫⠲⠸⠫⠈⠡⠻"] # 0x22A0 (Squared times) - - "⊡": [t: "⠫⠲⠸⠫⠡⠻"] # 0x22A1 (Squared dot operator) - - "⊢": [t: "⠫⠳⠒"] # 0x22A2 (Right tack) - - "⊣": [t: "⠫⠒⠳"] # 0x22A3 (Left tack) - - "⊤": [t: "⠫⠩⠳⠒"] # 0x22A4 (Down tack) -# - "⊥": [t: "⠀⠫⠏⠀ or ⠀⠫⠣⠳⠒⠀"] # 0x22A5 (Up tack (See also 27C2)) -# - "⊦": [t: ""] # 0x22A6 (Assertion) -# - "⊨": [t: ""] # 0x22A8 (TRUE) -# - "⊲": [t: ""] # 0x22B2 (Normal subgroup of) -# - "⊳": [t: ""] # 0x22B3 (Contains as normal subgroup) -# - "⊴": [t: ""] # 0x22B4 (Normal subgroup of or equal to) -# - "⊵": [t: ""] # 0x22B5 (Contains as normal subgroup or equal to) - - "⊶": [t: "⠫⠨⠡⠒⠡"] # 0x22B6 (Original of) - - "⊷": [t: "⠫⠡⠒⠨⠡"] # 0x22B7 (Image of) - - "⊸": [t: "⠫⠒⠨⠡"] # 0x22B8 (Multimap) -# - "⊾": [t: ""] # 0x22BE (Right angle w arc) - - "⊿": [t: "⠫⠞⠨⠗⠻"] # 0x22BF (Right triangle) - - "⋅": [t: "⠡"] # 0x22C5 (Dot operator) - - "∙": [t: "⠡"] # 0x2219 -- similar looking to 0x22c5, so using the same braille - - "⋆": [t: "⠫⠎"] # 0x22C6 (Star operator) - - "⋍": [t: "⠠⠱⠱"] # 0x22CD (Reversed tilde equals) - - "⋖": [t: "⠡⠈⠐⠅⠻"] # 0x22D6 (Less-than w dot) - - "⋗": [t: "⠡⠈⠨⠂⠻"] # 0x22D7 (Greater-than w dot) - - "⋘": [t: "⠐⠅⠈⠐⠅⠈⠐⠅⠻"] # 0x22D8 (Much less-than) - - "⋙": [t: "⠨⠂⠈⠨⠂⠈⠨⠂⠻"] # 0x22D9 (Much greater-than) - - "⋲": [t: "⠱⠈⠈⠑⠻"] # 0x22F2 (Element of w long horizontal stroke) - - "⋵": [t: "⠐⠈⠑⠣⠡⠻"] # 0x22F5 (Element of w dot above) - - "⋶": [t: "⠱⠈⠑"] # 0x22F6 (Element of w overbar) - - "⋺": [t: "⠱⠈⠈⠢⠻"] # 0x22FA (Contains w long horizontal stroke) - - "⋽": [t: "⠱⠈⠢"] # 0x22FD (Contains w overbar) - - "⌈": [t: "⠈⠘⠷"] # 0x2308 (Left ceiling) - - "⌉": [t: "⠈⠘⠾"] # 0x2309 (Right ceiling) - - "⌊": [t: "⠈⠰⠷"] # 0x230A (Left floor) - - "⌋": [t: "⠈⠰⠾"] # 0x230B (Right floor) - - "⌢": [t: "⠫⠁"] # 0x2322 (Frown) - - "⌣": [t: "⠫⠄"] # 0x2323 (Smile) - - "■": [t: "⠫⠸⠲"] # 0x25A0 (Filled square) - - "▫": [t: "⠫⠲"] # 0x25AB (Small white Square) - - "□": [t: "⠫⠲"] # 0x25A1 (Square) - - "◻": [t: "⠫⠲"] # 0x25FB (Square) - - "▬": [t: "⠫⠸⠗"] # 0x25AC (Filled rectangle) - - "▭": [t: "⠫⠗"] # 0x25AD (Rectangle) - - "▲": [t: "⠫⠸⠞"] # 0x25B2 (Black up-pointing triangle) - - "△": [t: "⠫⠞"] # 0x25B3 (White up-pointing triangle) - - "▼": [t: "⠸⠨⠫"] # 0x25BC (Black down-pointing triangle) - - "○": [t: "⠫⠉"] # 0x25CB (White circle) - - "●": [t: "⠫⠸⠉"] # 0x25CF (Black circle) - - "◫": [t: "⠫⠲⠸⠫⠳⠻"] # 0x25EB (White square w vertical bisecting line) -# - "⊥": [t: "⠫⠏"] # 0x27C2 (Perpendicular) - - "⟃": [t: "⠨⠡⠈⠸⠐⠅⠻"] # 0x27C3 (Open subset) - - "⟄": [t: "⠨⠡⠈⠸⠨⠂⠻"] # 0x27C4 (Open superset) - - "⟜": [t: "⠫⠨⠡⠒⠒"] # 0x27DC (Left multimap) - - "⟝": [t: "⠫⠳⠒⠒"] # 0x27DD (Long right tack) - - "⟞": [t: "⠫⠒⠒⠳"] # 0x27DE (Long left tack) - - "⟟": [t: "⠫⠣⠳⠒⠒⠨⠡"] # 0x27DF (Up tack w circle above) - - "⟦": [t: "⠈⠸⠷"] # 0x27E6 (Mathematical left white square bracket) - - "⟧": [t: "⠈⠸⠾"] # 0x27E7 (Mathematical right white square bracket) - - "⟨": [t: "⠨⠨⠷"] # 0x27E8 (Mathematical left angle bracket) - - "⟩": [t: "⠨⠨⠾"] # 0x27E9 (Mathematical right angle bracket) - - "⟵": [t: "⠀⠫⠪⠒⠒⠒⠀"] # 0x27F5 (Long leftwards arrow ) - - "⟶": [t: "⠀⠫⠒⠒⠒⠕⠀"] # 0x27F6 (Long rightwards arrow) - - "⟷": [t: "⠀⠫⠪⠒⠒⠒⠕⠀"] # 0x27F7 (Long left right arrow) - - "⟸": [t: "⠀⠫⠪⠶⠶⠶⠀"] # 0x27F8 (Long leftwards arrow ) - - "⟹": [t: "⠀⠫⠶⠶⠶⠕⠀"] # 0x27F9 (Long rightwards arrow) - - "⟺": [t: "⠀⠫⠪⠶⠶⠶⠕⠀"] # 0x27FA (Long left right arrow) - - "⟻": [t: "⠀⠫⠪⠒⠒⠒⠳⠀"] # 0x27FB (Long leftwards arrow from bar) - - "⟼": [t: "⠀⠫⠳⠒⠒⠒⠕⠀"] # 0x27FC (Long rightwards arrow from bar) - - "⟽": [t: "⠀⠫⠪⠶⠶⠶⠳⠀"] # 0x27FD (Long leftwards double arrow from bar) - - "⟾": [t: "⠀⠫⠳⠶⠶⠶⠕⠀"] # 0x27FE (Long rightwards double arrow from bar) - - "⟿": [t: "⠀⠫⠢⠤⠔⠒⠢⠤⠔⠒⠢⠕⠀"] # 0x27FF (Long rightwards squiggle arrow) - - "⤡": [t: "⠀⠫⠘⠪⠒⠒⠕⠀"] # 0x2921 (North west and south east arrow) - - "⤢": [t: "⠀⠫⠰⠪⠒⠒⠕⠀"] # 0x2922 (North east and south west arrow) - - "🣐": # 0x1f8d0 (Long rightwards arrow over leftwards arrow (equilibrium)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠠⠪⠒⠒"] - - "🣑": # 0x1f8d1 (Long rightwards harpoon over leftwards harpoon (equilibrium)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠠⠪⠒⠒"] - - "🣒": # 0x1f8d2 (Long rightwards harpoon over short leftwards harpoon (equilibrium)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] - - "🣓": # 0x1f8d3 (Short rightwards harpoon over long leftwards harpoon (equilibrium, trend to the right)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒⠒"] - else: [t: "⠫⠒⠈⠕⠫⠠⠪⠒⠒"] - - "🣔": # 0x1f8d4 (Long leftwards harpoon over short rightwards harpoon (equilibrium, trend to the left)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠪⠒⠒⠫⠒⠕"] - else: [t: "⠫⠈⠪⠒⠒⠫⠒⠠⠕"] - - "🣕": # 0x1f8d5 (Short leftwards harpoon over long rightwards harpoon (equilibrium, trend to the left)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠪⠒⠫⠒⠒⠕"] - else: [t: "⠫⠈⠪⠒⠫⠒⠒⠠⠕"] - - "⥂": # 0x2942 (Long rightwards harpoon over short leftwards harpoon (equilibrium)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠒⠕⠫⠪⠒"] - else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] - - "⥄": # 0x2944 (Short rightwards harpoon over long leftwards harpoon (equilibrium, trend to the right)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠒⠕⠫⠪⠒⠒"] - else: [t: "⠫⠒⠈⠕⠫⠠⠪⠒⠒"] - - "⥃": # 0x2943 (Long leftwards harpoon over short rightwards harpoon (equilibrium, trend to the left)) - - test: - if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" - then: [t: "⠫⠪⠒⠒⠫⠒⠕"] - else: [t: "⠫⠈⠪⠒⠒⠫⠒⠠⠕"] - - "🣖": [t: "⠌⠫⠒⠒⠕"] # 0x1f8d6 (long rightwards arrow with through X) - - "🣗": [t: "⠌⠫⠒⠒⠕"] # 0x1f8d7 (long rightwards arrow with double slash) chem code says it is same as 0x1f8d6 - - - - "⦃": [t: "⠨⠸⠷"] # 0x2983 (Left white curly bracket) - - "⦄": [t: "⠨⠸⠾"] # 0x2984 (Right white curly bracket) - - "⦑": [t: "⠡⠈⠨⠨⠷⠻"] # 0x2991 (Left angle bracket w dot) - - "⦒": [t: "⠡⠈⠨⠨⠾⠻"] # 0x2992 (Right angle bracket w dot) - - "⧄": [t: "⠫⠲⠸⠫⠔⠻"] # 0x29C4 (Square rising diagonal slash) - - "⧅": [t: "⠫⠲⠸⠫⠢⠻"] # 0x29C5 (Square falling diagonal slash) - - "⧆": [t: "⠫⠲⠸⠫⠈⠼⠻"] # 0x29C6 (Squared asterisk) - - "⧊": [t: "⠐⠫⠞⠣⠡⠻"] # 0x29CA (Triangle w dot above) - - "⧋": [t: "⠫⠞⠱"] # 0x29CB (Triangle w underbar) - - "⧌": [t: "⠫⠞⠎"] # 0x29CC (s in triangle §114) - - "⧦": [t: "⠫⠳⠶⠶⠳"] # 0x29E6 (Gleich stark) - - "⨌": [t: "⠮⠮⠮⠮"] # 0x2A0C (Quadruple integral operator) - - "⨍": [t: "⠮⠈⠱⠻"] # 0x2A0D (Finite part integral) - - "⨎": [t: "⠮⠈⠱⠱⠻"] # 0x2A0E (Integral w double stroke) - - "⨖": [t: "⠮⠈⠫⠲⠻"] # 0x2A16 (Quaternion integral operator) - - "⨘": [t: "⠮⠈⠈⠡⠻"] # 0x2A18 (Integral w times sign) - - "⨙": [t: "⠮⠈⠨⠩⠻"] # 0x2A19 (Integral w intersection) - - "⨚": [t: "⠮⠈⠨⠬⠻"] # 0x2A1A (Integral w union) - - "⨛": [t: "⠣⠮"] # 0x2A1B (Integral w overbar (upper)) - - "⨜": [t: "⠩⠮"] # 0x2A1C (Integral w underbar (lower)) - - "⨢": [t: "⠐⠬⠣⠨⠡⠻"] # 0x2A22 (Plus sign w small circle above) - - "⨣": [t: "⠐⠬⠣⠸⠣⠻"] # 0x2A23 (Plus sign w circumflex accent above) - - "⨤": [t: "⠐⠬⠣⠈⠱⠻"] # 0x2A24 (Plus sign w tilde above) - - "⨥": [t: "⠐⠬⠩⠡⠻"] # 0x2A25 (Plus sign w dot below) - - "⨪": [t: "⠐⠱⠩⠡⠻"] # 0x2A2A (Minus sign w dot below) - - "⨰": [t: "⠐⠈⠡⠣⠡⠻"] # 0x2A30 (Multiplication sign w dot above) - - "⨱": [t: "⠈⠡⠱"] # 0x2A31 (Multiplication sign w underbar) - - "⨸": [t: "⠫⠉⠸⠫⠨⠌⠻"] # 0x2A38 (Circled division sign) - - "⨹": [t: "⠫⠞⠸⠫⠬⠻"] # 0x2A39 (Plus sign in triangle) - - "⨺": [t: "⠫⠞⠸⠫⠤⠻"] # 0x2A3A (Minus sign in triangle) - - "⨻": [t: "⠫⠞⠸⠫⠈⠡⠻"] # 0x2A3B (Multiplication sign in triangle) - - "⩀": [t: "⠡⠈⠨⠩⠻"] # 0x2A40 (Intersection w dot) - - "⩁": [t: "⠤⠈⠨⠬⠻"] # 0x2A41 (Union w minus sign) - - "⩂": [t: "⠱⠨⠬"] # 0x2A42 (Union w overbar) - - "⩃": [t: "⠱⠨⠩"] # 0x2A43 (Intersection w overbar) - - "⩑": [t: "⠐⠈⠩⠣⠡⠻"] # 0x2A51 (Logical AND w dot above) - - "⩒": [t: "⠐⠈⠬⠣⠡⠻"] # 0x2A52 (Logical OR w dot above) - - "⩜": [t: "⠱⠈⠈⠩⠻"] # 0x2A5C (Logical AND with horizontal dash) - - "⩝": [t: "⠱⠈⠈⠬⠻"] # 0x2A5D (Logical OR with horizontal dash) - - "⩞": [t: "⠱⠱⠈⠩"] # 0x2A5E (Logical AND w double overbar) - - "⩟": [t: "⠈⠩⠱"] # 0x2A5F (Logical AND w underbar) - - "⩠": [t: "⠈⠩⠱⠱"] # 0x2A60 (Logical AND w double underbar) - - "⩢": [t: "⠱⠱⠈⠬"] # 0x2A62 (Logical OR w double overbar) - - "⩣": [t: "⠈⠬⠱⠱"] # 0x2A63 (Logical OR w double underbar) - - "⩦": [t: "⠐⠨⠅⠩⠡⠻"] # 0x2A66 (Equals sign w dot below) - - "⩧": [t: "⠐⠸⠇⠣⠡⠻"] # 0x2A67 (Identical w dot above) - - "⩪": [t: "⠐⠈⠱⠣⠡⠻"] # 0x2A6A (Tilde operator w dot above) - - "⩬": [t: "⠈⠱⠱⠈⠱"] # 0x2A6C (Similar minus similar) - - "⩭": [t: "⠐⠈⠱⠨⠅⠣⠡⠻"] # 0x2A6D (Congruent w dot above) - - "⩮": [t: "⠐⠨⠅⠣⠈⠼⠻"] # 0x2A6E (Equals w asterisk) - - "⩯": [t: "⠐⠈⠱⠈⠱⠣⠸⠣⠻"] # 0x2A6F (Almost equal to w circumflex) - - "⩰": [t: "⠈⠱⠈⠱⠨⠅"] # 0x2A70 (Approximately equal or equal to) - - "⩱": [t: "⠨⠅⠬"] # 0x2A71 (Equals sign above plus sign) - - "⩲": [t: "⠬⠨⠅"] # 0x2A72 (Plus sign above equals sign) - - "⩳": [t: "⠨⠅⠈⠱"] # 0x2A73 (Equals sign above tilde operator) - - "⩴": [t: "⠐⠂⠐⠂⠨⠅"] # 0x2A74 (Double colon equal) - - "⩵": [t: "⠨⠅⠨⠅"] # 0x2A75 (Two consecutive equals signs) - - "⩶": [t: "⠨⠅⠨⠅⠨⠅"] # 0x2A76 (Three consecutive equals signs) - - "⩷": [t: "⠐⠨⠅⠩⠡⠡⠣⠡⠡⠻"] # 0x2A77 (Equals sign w two dots above and two dots below) - - "⩸": [t: "⠐⠸⠇⠣⠡⠡⠡⠡⠻"] # 0x2A78 (Equivalent w four dots above) - - "⩹": [t: "⠐⠅⠈⠨⠡⠻"] # 0x2A79 (Less than w circle inside) - - "⩺": [t: "⠨⠂⠈⠨⠡⠻"] # 0x2A7A (Greater than w circle inside) - - "⩻": [t: "⠐⠐⠅⠣⠸⠦⠻"] # 0x2A7B (Less-than w question mark above) - - "⩼": [t: "⠐⠨⠂⠣⠸⠦⠻"] # 0x2A7C (Greater-than w question mark above) - - "⪅": [t: "⠐⠅⠈⠱⠈⠱"] # 0x2A85 (Less-than or approximate) - - "⪆": [t: "⠨⠂⠈⠱⠈⠱"] # 0x2A86 (Greater-than or approximate) - - "⪋": [t: "⠐⠅⠨⠅⠨⠂"] # 0x2A8B (Less-than above double-line equal above greater-than) - - "⪌": [t: "⠨⠂⠨⠅⠐⠅"] # 0x2A8C (Greater-than above double-line equal above less-than) - - "⪍": [t: "⠐⠅⠈⠱⠱"] # 0x2A8D (Less-than above similar or equal to) - - "⪎": [t: "⠨⠂⠈⠱⠱"] # 0x2A8E (Greater-than above similar or equal to) - - "⪏": [t: "⠐⠅⠈⠱⠨⠂"] # 0x2A8F (Less-than above similar above greater-than) - - "⪐": [t: "⠨⠂⠈⠱⠐⠅"] # 0x2A90 (Greater-than above similar above less-than) - - "⪑": [t: "⠐⠅⠨⠂⠨⠅"] # 0x2A91 (Less-than above greater-than above double-line equal) - - "⪒": [t: "⠨⠂⠐⠅⠨⠅"] # 0x2A92 (Greater-than above less-than above double-line equal) - - "⪙": [t: "⠨⠅⠐⠅"] # 0x2A99 (Double-line equal to or less-than) - - "⪚": [t: "⠨⠅⠨⠂"] # 0x2A9A (Double-line equal to or greater-than) - - "⪝": [t: "⠈⠱⠐⠅"] # 0x2A9D (Similar or less-than) - - "⪞": [t: "⠈⠱⠨⠂"] # 0x2A9E (Similar or greater-than) - - "⪟": [t: "⠈⠱⠐⠅⠨⠅"] # 0x2A9F (Similar above less-than above equals sign) - - "⪠": [t: "⠈⠱⠨⠂⠨⠅"] # 0x2AA0 (Similar above greater-than above equals sign) - - "⪮": [t: "⠈⠣⠨⠅"] # 0x2AAE (Equals sign with bumpy above) - - "⪽": [t: "⠡⠈⠸⠐⠅⠻"] # 0x2ABD (Subset w dot) - - "⪾": [t: "⠡⠈⠸⠨⠂⠻"] # 0x2ABE (Superset w dot) - - "⪿": [t: "⠐⠸⠐⠅⠩⠬⠻"] # 0x2ABF (Subset with plus sign below) - - "⫀": [t: "⠐⠸⠨⠂⠩⠬⠻"] # 0x2AC0 (Superset with plus sign below) - - "⫁": [t: "⠐⠸⠐⠅⠩⠈⠡⠻"] # 0x2AC1 (Subset with multiplication sign below) - - "⫂": [t: "⠐⠸⠨⠂⠩⠈⠡⠻"] # 0x2AC2 (Superset with multiplication sign below) - - "⫅": [t: "⠸⠐⠅⠨⠅"] # 0x2AC5 (Subset of above equals sign) - - "⫆": [t: "⠸⠨⠂⠨⠅"] # 0x2AC6 (Superset of above equals sign) - - "⫇": [t: "⠸⠐⠅⠈⠱"] # 0x2AC7 (Subset of above tilde operator) - - "⫈": [t: "⠸⠨⠂⠈⠱"] # 0x2AC8 (Superset of above tilde operator) - - "⫉": [t: "⠸⠐⠅⠈⠱⠈⠱"] # 0x2AC9 (Subset of above almost equal to) - - "⫊": [t: "⠸⠨⠂⠈⠱⠈⠱"] # 0x2ACA (Superset of above almost equal to) - - "⫋": [t: "⠸⠐⠅⠌⠨⠅"] # 0x2ACB (Subset of above not equals sign) - - "⫌": [t: "⠸⠨⠂⠌⠨⠅"] # 0x2ACC (Superset of above not equals sign) - - "⫓": [t: "⠸⠐⠅⠸⠨⠂"] # 0x2AD3 (Subset above superset) - - "⫔": [t: "⠸⠨⠂⠸⠐⠅"] # 0x2AD4 (Superset above subset) - - "⫕": [t: "⠸⠐⠅⠸⠐⠅"] # 0x2AD5 (Subset above subset) - - "⫖": [t: "⠸⠨⠂⠸⠨⠂"] # 0x2AD6 (Superset above superset) - - "⫗": [t: "⠸⠨⠂⠐⠸⠐⠅"] # 0x2AD7 (Superset beside subset) - - "⫯": [t: "⠫⠩⠨⠡⠒⠒"] # 0x2AEF (Vertical line w circle above) - - "⫰": [t: "⠫⠣⠨⠡⠒⠒"] # 0x2AF0 (Vertical line w circle below) - - "⫱": [t: "⠫⠣⠨⠡⠒⠒⠳"] # 0x2AF1 (Down tack w circle below) - - "⫲": [t: "⠱⠈⠫⠇⠻"] # 0x2AF2 (Parallel w horizontal stroke) - - "⫳": [t: "⠈⠱⠈⠫⠇⠻"] # 0x2AF3 (Parallel w tilde operator) - - "⫴": [t: "⠳⠳⠳"] # 0x2AF4 (Triple vertical bar binary relation) - - "⬬": [t: "⠫⠸⠑"] # 0x2B2C (Black ellipse) - - "⬭": [t: "⠫⠑"] # 0x2B2D (White ellipse) - - "⬰": [t: "⠨⠡⠈⠫⠪⠒⠒⠻"] # 0x2B30 (Left arrow w small circle) - - "⬱": [t: "⠫⠪⠒⠒⠫⠪⠒⠒⠫⠪⠒⠒"] # 0x2B31 (Three leftwards arrows) - - "⬲": [t: "⠫⠪⠒⠒⠈⠫⠉⠸⠫⠬⠻"] # 0x2B32 (Left arrow w circled plus) - - "⮖": [t: "⠨⠡"] # 0x2B96 (Standard State Symbol) - - "⯃": [t: "⠫⠸⠦"] # 0x2BC3 (Horizontal black octagon) - - # SRE lists this as a custom transcription - - "⋊": [t: "⠈⠡⠳"] # 0x22ca (RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT) +--- +# To greatly simplify typeface/language generation, the chars have unique ASCII chars for them: +# Typeface: S: sans-serif, B: bold, T: script/blackboard, I: italic, R: Roman +# Language: E: English, D: German, G: Greek, V: Greek variants, H: Hebrew, U: Russian +# Indicators: C: capital, L: letter, N: number, P: punctuation, M: multipurpose +# Others: W -- whitespace that should be kept (e.g, in a numeral) +# SRE doesn't have H: Hebrew, so not encoded (yet) +# Some of the weird letters (e.g., circled letters) I didn't translate with the above because +# they don't interact with rules since they are enclosed in other chars. + + - "⨯": [t: "⠈⠡"] # U+2A2F(VECTOR OR CROSS PRODUCT) -- make the same as 0x00D7 (Multiplication sign) + + # --- Nemeth Default Alphabet bold chars. --- + - "𝐀-𝐙": # 0x1d400 - 0x1d419 + - tc: "BE" + - spell: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "𝐚-𝐳": # 0x1d41a - 0x1d433 + - tc: "BE" + - spell: "translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 + - tc: "BG" + - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝛂-𝛒": # 0x1d6c2 - 0x1d6d2 + - tc: "BG" + - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒', 'αβγδεζηθικλμνξοπρ')" + + - "𝛔-𝛖": # 0x1d6d4 - 0x1d6d6 + - tc: "BG" + - spell: "translate('.', '𝛔𝛕𝛖', 'στυ')" + + - "𝛘-𝛚": # 0x1d6d8 - 0x1d6da + - tc: "BG" + - spell: "translate('.', '𝛘𝛙𝛚', 'χψω')" + + - "𝛁": [tc: "BGL⠫"] # 0x1d6c1 + - "𝛓": [tc: "BVL⠎"] # 0x1d6d3 + - "𝛗": [tc: "BVL⠋"] # 0x1d6d7 + - "𝛛": [tc: "⠸⠈⠙"] # 0x1d6db + - "𝛜": [tc: "BGL⠑"] # 0x1d6dc + - "𝛝": [tc: "BVL⠹"] # 0x1d6dd + - "𝛞": [tc: "BGL⠅"] # 0x1d6de + - "𝛟": [tc: "BGL⠋"] # 0x1d6df + - "𝛠": [tc: "BGL⠗"] # 0x1d6e0 + - "𝛡": [tc: "BGL⠏"] # 0x1d6e1 + + - "𝟎-𝟗": # 0x1d7ce - 0x1d7d7 + - tc: "B" + - spell: "translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789')" + + # --- Nemeth Default Alphabet bold-fraktur chars. --- + + - "𝕬-𝖅": # 0x1d56c - 0x1d585 + - tc: "BD" + - spell: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝖆-𝖟": # 0x1d586 - 0x1d59f + - tc: "BD" + - spell: "translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz')" + + # --- Nemeth Default Alphabet bold-italic chars. --- + - "𝑨-𝒁": # 0x1d468 - 0x1d481 + - tc: "BE" + - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝒂-𝒛": # 0x1d482 - 0x1d49b + - tc: "BE" + - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝜜-𝜴": # 0x1d71c - 0x1d734 + - tc: "BG" + - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳𝜴', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝜵": [tc: "BGL⠫"] # 0x1d735 + + - "𝜶-𝝆": # 0x1d736 - 0x1d746 + - tc: "BG" + - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆', 'αβγδεζηθικλμνξοπρ')" + + - "𝝇": [tc: "BVL⠎"] # 0x1d747 + + - "𝝈-𝝊": # 0x1d748 - 0x1d74a + - tc: "BG" + - spell: "translate('.', '𝝈𝝉𝝊', 'στυ')" + + - "𝝋": [tc: "BVL⠋"] # 0x1d74b + + - "𝝌-𝝎": # 0x1d74c - 0x1d74e + - tc: "BG" + - spell: "translate('.', '𝝌𝝍𝝎', 'χψω')" + + - "𝝏": [tc: "BVL⠙"] # 0x1d74f + - "𝝐": [tc: "BGL⠑"] # 0x1d750 + - "𝝑": [tc: "BVL⠹"] # 0x1d751 + + - "𝝒-𝝕": # 0x1d752 - 0x1d755 + - tc: "BG" + - spell: "translate('.', '𝝒𝝓𝝔𝝕', 'κφρπ')" + + # --- Nemeth Default Alphabet bold-script chars. --- + + - "𝓐-𝓩": # 0x1d4d0 - 0x1d4e9 + - tc: "BTE" + - spell: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝓪-𝔃": # 0x1d4ea - 0x1d503 + - tc: "BTE" + - spell: "translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝚤-𝚥": # 0x1d6a4 - 0x1d6a5 + - tc: "" + - spell: "translate('.', '𝚤𝚥', '⠊⠚')" + +# Russian Alphabet (from Nemeth book) +# NOTE: is this translation needed? + - "А-Я": # 0x410 - 0x42f + - tc: "U" + - spell: "translate('.', 'АБВГДЕЗИЙКЛМНОПРСТУФЭ', 'ABVGDEZIYKLMNOPRSTUFE')" + + - "а-я": # 0x430 - 0x44f + - tc: "U" + - spell: "translate('.', 'абвгдезийклмнопрстуфэ', 'abvgdeziylkmnoprstufye')" + + - "①-⑨": # 0x2460 - 0x2473 + - tc: "⠫⠉⠸⠫⠼" + - spell: "translate('.', '①②③④⑤⑥⑦⑧⑨', '123456789')" + - tc: "⠻" + + - "⓪": [tc: "⠫⠉⠸⠫⠼⠴⠻"] # 0x24ea + + - "⑩": [tc: "⠫⠉⠸⠫⠼⠂⠴⠻"] # 0x2469 + - "⑪": [tc: "⠫⠉⠸⠫⠼⠂⠂⠻"] # 0x246a + - "⑫": [tc: "⠫⠉⠸⠫⠼⠂⠆⠻"] # 0x246b + - "⑬": [tc: "⠫⠉⠸⠫⠼⠂⠒⠻"] # 0x246c + - "⑭": [tc: "⠫⠉⠸⠫⠼⠂⠲⠻"] # 0x246d + - "⑮": [tc: "⠫⠉⠸⠫⠼⠂⠢⠻"] # 0x246e + - "⑯": [tc: "⠫⠉⠸⠫⠼⠂⠖⠻"] # 0x246f + - "⑰": [tc: "⠫⠉⠸⠫⠼⠂⠶⠻"] # 0x2470 + - "⑱": [tc: "⠫⠉⠸⠫⠼⠂⠦⠻"] # 0x2471 + - "⑲": [tc: "⠫⠉⠸⠫⠼⠂⠔⠻"] # 0x2472 + - "⑳": [tc: "⠫⠉⠸⠫⠼⠆⠴⠻"] # 0x2473 + + - "㉑": [tc: "⠫⠉⠸⠫⠼⠆⠂⠻"] # 0x3251 + - "㉒": [tc: "⠫⠉⠸⠫⠼⠆⠆⠻"] # 0x3252 + - "㉓": [tc: "⠫⠉⠸⠫⠼⠆⠒⠻"] # 0x3253 + - "㉔": [tc: "⠫⠉⠸⠫⠼⠆⠲⠻"] # 0x3254 + - "㉕": [tc: "⠫⠉⠸⠫⠼⠆⠢⠻"] # 0x3255 + - "㉖": [tc: "⠫⠉⠸⠫⠼⠆⠖⠻"] # 0x3256 + - "㉗": [tc: "⠫⠉⠸⠫⠼⠆⠶⠻"] # 0x3257 + - "㉘": [tc: "⠫⠉⠸⠫⠼⠆⠦⠻"] # 0x3258 + - "㉙": [tc: "⠫⠉⠸⠫⠼⠆⠔⠻"] # 0x3259 + - "㉚": [tc: "⠫⠉⠸⠫⠼⠒⠴⠻"] # 0x325a + - "㉛": [tc: "⠫⠉⠸⠫⠼⠒⠂⠻"] # 0x325b + - "㉜": [tc: "⠫⠉⠸⠫⠼⠒⠆⠻"] # 0x325c + - "㉝": [tc: "⠫⠉⠸⠫⠼⠒⠒⠻"] # 0x325d + - "㉞": [tc: "⠫⠉⠸⠫⠼⠒⠲⠻"] # 0x325e + - "㉟": [tc: "⠫⠉⠸⠫⠼⠒⠢⠻"] # 0x325f + - "㊱": [tc: "⠫⠉⠸⠫⠼⠒⠖⠻"] # 0x32b1 + - "㊲": [tc: "⠫⠉⠸⠫⠼⠒⠶⠻"] # 0x32b2 + - "㊳": [tc: "⠫⠉⠸⠫⠼⠒⠦⠻"] # 0x32b3 + - "㊴": [tc: "⠫⠉⠸⠫⠼⠒⠔⠻"] # 0x32b4 + - "㊵": [tc: "⠫⠉⠸⠫⠼⠲⠴⠻"] # 0x32b5 + - "㊶": [tc: "⠫⠉⠸⠫⠼⠲⠂⠻"] # 0x32b6 + - "㊷": [tc: "⠫⠉⠸⠫⠼⠲⠆⠻"] # 0x32b7 + - "㊸": [tc: "⠫⠉⠸⠫⠼⠲⠒⠻"] # 0x32b8 + - "㊹": [tc: "⠫⠉⠸⠫⠼⠲⠲⠻"] # 0x32b9 + - "㊺": [tc: "⠫⠉⠸⠫⠼⠲⠢⠻"] # 0x32ba + - "㊻": [tc: "⠫⠉⠸⠫⠼⠲⠖⠻"] # 0x32bb + - "㊼": [tc: "⠫⠉⠸⠫⠼⠲⠶⠻"] # 0x32bc + - "㊽": [tc: "⠫⠉⠸⠫⠼⠲⠦⠻"] # 0x32bd + - "㊾": [tc: "⠫⠉⠸⠫⠼⠲⠔⠻"] # 0x32be + - "㊿": [tc: "⠫⠉⠸⠫⠼⠢⠴⠻"] # 0x32bf + +#NOTE: uncertainty here + - "Ⓐ-Ⓩ": # 0x24b6 - 0x24cf + - tc: "⠫⠉⠸⠫⠠" + - spell: "translate('.', 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - tc: "⠻" + + - "ⓐ-ⓩ": # 0x24d0 - 0x24e9 + - tc: "⠫⠉⠸⠫" + - spell: "translate('.', 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ', 'abcdefghijklmnopqrstuvwxyz')" + - tc: "⠻" + + # --- Nemeth Default Alphabet circled-sans-serif chars. --- + + - "➀-➈": # 0x1f10b, 0x2780 - 0x2788 + - tc: "⠫⠉⠸⠫⠠⠨⠼" + - spell: "translate('.', '➀➁➂➃➄➅➆➇➈', '123456789')" + - tc: "⠻" + + - "🄋": [tc: "⠫⠉⠸⠫⠠⠨⠼⠴⠻"] # 0x1f10b + - "➉": [tc: "⠫⠉⠸⠫⠠⠨⠼⠂⠴⠻"] # 0x2789 + + # --- Nemeth Default Alphabet comma chars. --- + + - "🄁-🄊": # 0x1f101 - 0x1f10a + - spell: "translate('.', '🄁🄂🄃🄄🄅🄆🄇🄈🄉🄊', '0123456789')" + - tc: "⠠" + + + # --- Nemeth Default Alphabet double-circled chars. --- + + - "⓵": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠂⠻⠻"] # 0x24f5 + - "⓶": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠆⠻⠻"] # 0x24f6 + - "⓷": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠒⠻⠻"] # 0x24f7 + - "⓸": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠲⠻⠻"] # 0x24f8 + - "⓹": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠢⠻⠻"] # 0x24f9 + - "⓺": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠖⠻⠻"] # 0x24fa + - "⓻": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠶⠻⠻"] # 0x24fb + - "⓼": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠦⠻⠻"] # 0x24fc + - "⓽": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠔⠻⠻"] # 0x24fd + - "⓾": [t: "⠫⠉⠸⠫⠫⠉⠸⠫⠼⠂⠴⠻⠻"] # 0x24fe + + # --- Nemeth Default Alphabet double-struck chars. --- + - "𝔸-𝕐": # 0x1d538 - 0x1d550 (partial, excludes ℂ ℍ ℕ ℙ ℚ ℝ ℤ) + - tc: "𝔹E" + - spell: "translate('.', '𝔸𝔹𝔻𝔼𝔽𝔾𝕀𝕁𝕂𝕃𝕄𝕆𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'ABDEFGIJKLMOSTUVWXY')" + + - "ℂ": [tc: "𝔹ECL⠉"] # 0x2102 + - "ℍ": [tc: "𝔹ECL⠓"] # 0x210d + - "ℕ": [tc: "𝔹ECL⠝"] # 0x2115 + - "ℙ": [tc: "𝔹ECL⠏"] # 0x2119 + - "ℚ": [tc: "𝔹ECL⠟"] # 0x211a + - "ℝ": [tc: "𝔹ECL⠗"] # 0x211d + - "ℤ": [tc: "𝔹ECL⠵"] # 0x2124 + - "Ω": [t: "CGL⠺"] # 0x2126 (Ohm sign (capital Greek omega)) + + - "𝕒-𝕫": # 0x1d552 - 0x1d56b + - tc: "𝔹E" + - spell: "translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝟘-𝟡": # 0x1d7d8 - 0x1d7e1 + - tc: "𝔹" + - spell: "translate('.', '𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡', '0123456789')" + + + # --- Nemeth Default Alphabet fraktur chars. --- + + - "𝔄-𝔜": # 0x1d504 - 0x1d51c + - tc: "D" + - spell: "translate('.', '𝔄𝔅𝔇𝔈𝔉𝔊𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔖𝔗𝔘𝔙𝔚𝔛𝔜', 'ABDEFGJKLMNOPQSTUVWXY')" + + - "ℭ": [tc: "DCL⠉"] # 0x212d + - "ℌ": [tc: "DCL⠓"] # 0x210c + - "ℑ": [tc: "DCL⠊"] # 0x2111 + - "ℜ": [tc: "DCL⠗"] # 0x211c + - "ℨ": [tc: "DCL⠵"] # 0x2128 + + - "𝔞-𝔷": # 0x1d51e - 0x1d537 + - tc: "D" + - spell: "translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz')" + + # --- Nemeth Default Alphabet fullwidth chars. --- + + - "A-Z": # 0xff21 - 0xff3a + - tc: "" + - spell: "translate('.', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "a-z": # 0xff41 - 0xff5a + - tc: "" + - spell: "translate('.', 'abcdefghijklmnopqrstuvwxyz', 'abcdefghijklmnopqrstuvwxyz')" + + - "0-9": # 0xff10 - 0xff19 + - tc: "" + - spell: "translate('.', '0123456789', '0123456789')" + + # --- Nemeth Default Alphabet italic chars -- treat as non-italic. --- + + - "𝐴-𝑍": # 0x1d434 - 0x1d44d + - tc: "E" + - spell: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝑎-𝑧": # 0x1d44e - 0x1d467 + - tc: "E" + - spell: "translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔ℎ𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝛢-𝛺": # 0x1d6e2 - 0x1d6fa + - tc: "G" + - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹𝛺', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝛻": [tc: "GL⠫"] # 0x1d6fb + + - "𝛼-𝜌": # 0x1d6fc - 0x1d70c + - tc: "G" + - spell: "translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌', 'αβγδεζηθικλμνξοπρ')" + - "𝜍": [tc: "VL⠎"] # 0x1d70d + + - "𝜎": [tc: "GL⠎"] # 0x1d70e + - "𝜏": [tc: "GL⠞"] # 0x1d70f + - "𝜐": [tc: "GL⠥"] # 0x1d710 + + - "𝜑": [tc: "VL⠋"] # 0x1d711 + + - "𝜒": [tc: "GL⠯"] # 0x1d712 + - "𝜓": [tc: "GL⠽"] # 0x1d713 + - "𝜔": [tc: "GL⠺"] # 0x1d714 + + - "𝜕": [tc: "VL⠙"] # 0x1d715 + + - "𝜖": [tc: "GL⠑"] # 0x1d716 + + - "𝜗": [tc: "VL⠹"] # 0x1d717 + + - "𝜘": [tc: "GL⠅"] # 0x1d718 + - "𝜙": [tc: "GL⠋"] # 0x1d719 + - "𝜚": [tc: "GL⠗"] # 0x1d71a + - "𝜛": [tc: "GL⠏"] # 0x1d71b + + # --- Nemeth Default Alphabet monospace chars. --- + + - "𝙰-𝚉": # 0x1d670 - 0x1d689 + - tc: "" + - spell: "translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝚊-𝚣": # 0x1d68a - 0x1d6a3 + - tc: "" + - spell: "translate('.', '𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝟶-𝟿": # 0x1d7f6 - 0x1d7ff + - tc: "" + - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" + + + # --- Nemeth Default Alphabet negative-circled chars. --- + - "⓿": [t: "⠫⠸⠉⠸⠫⠼⠴⠻"] # 0x24ff + - "❶": [t: "⠫⠸⠉⠸⠫⠼⠂⠻"] # 0x2776 + - "❷": [t: "⠫⠸⠉⠸⠫⠼⠆⠻"] # 0x2777 + - "❸": [t: "⠫⠸⠉⠸⠫⠼⠒⠻"] # 0x2778 + - "❹": [t: "⠫⠸⠉⠸⠫⠼⠲⠻"] # 0x2779 + - "❺": [t: "⠫⠸⠉⠸⠫⠼⠢⠻"] # 0x277a + - "❻": [t: "⠫⠸⠉⠸⠫⠼⠖⠻"] # 0x277b + - "❼": [t: "⠫⠸⠉⠸⠫⠼⠶⠻"] # 0x277c + - "❽": [t: "⠫⠸⠉⠸⠫⠼⠦⠻"] # 0x277d + - "❾": [t: "⠫⠸⠉⠸⠫⠼⠔⠻"] # 0x277e + - "❿": [t: "⠫⠸⠉⠸⠫⠼⠂⠴⠻"] # 0x277f + - "⓫": [t: "⠫⠸⠉⠸⠫⠼⠂⠂⠻"] # 0x24eb + - "⓬": [t: "⠫⠸⠉⠸⠫⠼⠂⠆⠻"] # 0x24ec + - "⓭": [t: "⠫⠸⠉⠸⠫⠼⠂⠒⠻"] # 0x24ed + - "⓮": [t: "⠫⠸⠉⠸⠫⠼⠂⠲⠻"] # 0x24ee + - "⓯": [t: "⠫⠸⠉⠸⠫⠼⠂⠢⠻"] # 0x24ef + - "⓰": [t: "⠫⠸⠉⠸⠫⠼⠂⠖⠻"] # 0x24f0 + - "⓱": [t: "⠫⠸⠉⠸⠫⠼⠂⠶⠻"] # 0x24f1 + - "⓲": [t: "⠫⠸⠉⠸⠫⠼⠂⠦⠻"] # 0x24f2 + - "⓳": [t: "⠫⠸⠉⠸⠫⠼⠂⠔⠻"] # 0x24f3 + - "⓴": [t: "⠫⠸⠉⠸⠫⠼⠆⠴⠻"] # 0x24f4 + + - "🅐": [t: "⠫⠸⠉⠸⠫⠠⠁⠻"] # 0x1f150 + - "🅑": [t: "⠫⠸⠉⠸⠫⠠⠃⠻"] # 0x1f151 + - "🅒": [t: "⠫⠸⠉⠸⠫⠠⠉⠻"] # 0x1f152 + - "🅓": [t: "⠫⠸⠉⠸⠫⠠⠙⠻"] # 0x1f153 + - "🅔": [t: "⠫⠸⠉⠸⠫⠠⠑⠻"] # 0x1f154 + - "🅕": [t: "⠫⠸⠉⠸⠫⠠⠋⠻"] # 0x1f155 + - "🅖": [t: "⠫⠸⠉⠸⠫⠠⠛⠻"] # 0x1f156 + - "🅗": [t: "⠫⠸⠉⠸⠫⠠⠓⠻"] # 0x1f157 + - "🅘": [t: "⠫⠸⠉⠸⠫⠠⠊⠻"] # 0x1f158 + - "🅙": [t: "⠫⠸⠉⠸⠫⠠⠚⠻"] # 0x1f159 + - "🅚": [t: "⠫⠸⠉⠸⠫⠠⠅⠻"] # 0x1f15a + - "🅛": [t: "⠫⠸⠉⠸⠫⠠⠇⠻"] # 0x1f15b + - "🅜": [t: "⠫⠸⠉⠸⠫⠠⠍⠻"] # 0x1f15c + - "🅝": [t: "⠫⠸⠉⠸⠫⠠⠝⠻"] # 0x1f15d + - "🅞": [t: "⠫⠸⠉⠸⠫⠠⠕⠻"] # 0x1f15e + - "🅟": [t: "⠫⠸⠉⠸⠫⠠⠏⠻"] # 0x1f15f + - "🅠": [t: "⠫⠸⠉⠸⠫⠠⠟⠻"] # 0x1f160 + - "🅡": [t: "⠫⠸⠉⠸⠫⠠⠗⠻"] # 0x1f161 + - "🅢": [t: "⠫⠸⠉⠸⠫⠠⠎⠻"] # 0x1f162 + - "🅣": [t: "⠫⠸⠉⠸⠫⠠⠞⠻"] # 0x1f163 + - "🅤": [t: "⠫⠸⠉⠸⠫⠠⠥⠻"] # 0x1f164 + - "🅥": [t: "⠫⠸⠉⠸⠫⠠⠧⠻"] # 0x1f165 + - "🅦": [t: "⠫⠸⠉⠸⠫⠠⠺⠻"] # 0x1f166 + - "🅧": [t: "⠫⠸⠉⠸⠫⠠⠭⠻"] # 0x1f167 + - "🅨": [t: "⠫⠸⠉⠸⠫⠠⠽⠻"] # 0x1f168 + - "🅩": [t: "⠫⠸⠉⠸⠫⠠⠵⠻"] # 0x1f169 + + # --- Nemeth Default Alphabet negative-circled-sans-serif chars. --- + - "🄌": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠴⠻"] # 0x1f10c + - "➊": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠂⠻"] # 0x278a + - "➋": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠆⠻"] # 0x278b + - "➌": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠒⠻"] # 0x278c + - "➍": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠲⠻"] # 0x278d + - "➎": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠢⠻"] # 0x278e + - "➏": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠖⠻"] # 0x278f + - "➐": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠶⠻"] # 0x2790 + - "➑": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠦⠻"] # 0x2791 + - "➒": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠔⠻"] # 0x2792 + - "➓": [t: "⠫⠸⠉⠸⠫⠠⠨⠼⠂⠴⠻"] # 0x2793 + + # --- Nemeth Default Alphabet negative-squared chars. --- + - "🅰-🆉": # 0x1f170 - 0x1f189 + - tc: "⠫⠸⠲⠸⠫⠠" + - spell: "translate('.', '🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - tc: "⠻" + + # --- Nemeth Default Alphabet parenthesized chars. --- + + - "⑴-⑼": # 0x2474 - 0x247c + - tc: "⠷" + - spell: "translate('.', '⑴⑵⑶⑷⑸⑹⑺⑻⑼', '123456789')" + - tc: "⠾" + + - "⑽": [t: "⠷⠂⠴⠾"] # 0x247d + - "⑾": [t: "⠷⠂⠂⠾"] # 0x247e + - "⑿": [t: "⠷⠂⠆⠾"] # 0x247f + - "⒀": [t: "⠷⠂⠒⠾"] # 0x2480 + - "⒁": [t: "⠷⠂⠲⠾"] # 0x2481 + - "⒂": [t: "⠷⠂⠢⠾"] # 0x2482 + - "⒃": [t: "⠷⠂⠖⠾"] # 0x2483 + - "⒄": [t: "⠷⠂⠶⠾"] # 0x2484 + - "⒅": [t: "⠷⠂⠦⠾"] # 0x2485 + - "⒆": [t: "⠷⠂⠔⠾"] # 0x2486 + - "⒇": [t: "⠷⠆⠴⠾"] # 0x2487 + + - "🄐-🄩": # 0x1f110 - 0x1f129 + - tc: "⠷⠠" + - spell: "translate('.', '🄐🄑🄒🄓🄔🄕🄖🄗🄘🄙🄚🄛🄜🄝🄞🄟🄠🄡🄢🄣🄤🄥🄦🄧🄨🄩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - tc: "⠾" + + - "⒜-⒵": # 0x249c - 0x24b5 + - tc: "⠷" + - spell: "translate('.', '⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵', 'abcdefghijklmnopqrstuvwxyz')" + - tc: "⠾" + + # --- Nemeth Default Alphabet period chars. --- + - "🄀": [t: "⠴⠸⠲"] # 0x1f100 + - "⒈": [t: "⠂⠸⠲"] # 0x2488 + - "⒉": [t: "⠆⠸⠲"] # 0x2489 + - "⒊": [t: "⠒⠸⠲"] # 0x248a + - "⒋": [t: "⠲⠸⠲"] # 0x248b + - "⒌": [t: "⠢⠸⠲"] # 0x248c + - "⒍": [t: "⠖⠸⠲"] # 0x248d + - "⒎": [t: "⠶⠸⠲"] # 0x248e + - "⒏": [t: "⠦⠸⠲"] # 0x248f + - "⒐": [t: "⠔⠸⠲"] # 0x2490 + + - "⒑": [t: "⠂⠴⠸⠲"] # 0x2491 + - "⒒": [t: "⠂⠂⠸⠲"] # 0x2492 + - "⒓": [t: "⠂⠆⠸⠲"] # 0x2493 + - "⒔": [t: "⠂⠒⠸⠲"] # 0x2494 + - "⒕": [t: "⠂⠲⠸⠲"] # 0x2495 + - "⒖": [t: "⠂⠢⠸⠲"] # 0x2496 + - "⒗": [t: "⠂⠖⠸⠲"] # 0x2497 + - "⒘": [t: "⠂⠶⠸⠲"] # 0x2498 + - "⒙": [t: "⠂⠦⠸⠲"] # 0x2499 + - "⒚": [t: "⠂⠔⠸⠲"] # 0x249a + - "⒛": [t: "⠆⠴⠸⠲"] # 0x249b + + # --- Nemeth Default Alphabet sans-serif chars. --- + - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 + - tc: "SE" + - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝖺-𝗓": # 0x1d5ba - 0x1d5d3 + - tc: "SE" + - spell: "translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝟢-𝟫": # 0x1d7e2 - 0x1d7eb + - tc: "S" + - spell: "translate('.', '𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫', '0123456789')" + + # --- Nemeth Default Alphabet sans-serif-bold chars. --- + - "𝗔-𝗭": # 0x1d5d4 - 0x1d5ed + - tc: "SBE" + - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝗮-𝘇": # 0x1d5ee - 0x1d607 + - tc: "SBE" + - spell: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝝖-𝝮": # 0x1d756 - 0x1d76e + - tc: "SBG" + - spell: "translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭𝝮', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝝰-𝞀": # 0x1d770 - 0x1d780 + - tc: "SBG" + - spell: "translate('.', '𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿𝞀', 'αβγδεζηθικλμνξοπρ')" + + - "𝝯": [tc: "SBGL⠫"] # 0x1d76f + + - "𝞁": [tc: "SBVL⠎"] # 0x1d781 + + - "𝞂-𝞄": # 0x1d782 - 0x1d784 + - tc: "SBG" + - spell: "translate('.', '𝞂𝞃𝞄', 'στυ')" + + - "𝞅": [tc: "SBVL⠋"] # 0x1d785 + + - "𝞆-𝞈": # 0x1d786 - 0x1d788 + - tc: "SBG" + - spell: "translate('.', '𝞆𝞇𝞈', 'χψω')" + + - "𝞉": [tc: "⠠⠨⠸⠈⠙"] # 0x1d789 + - "𝞊": [tc: "SBGL⠑"] # 0x1d78a + - "𝞋": [tc: "SBVL⠹"] # 0x1d78b + + - "𝞌-𝞏": # 0x1d78c - 0x1d78f + - tc: "SBG" + - spell: "translate('.', '𝞌𝞍𝞎𝞏', 'κφρπ')" + + - "𝟬-𝟵": # 0x1d7ec - 0x1d7f5 + - tc: "SB" + - spell: "translate('.', '𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵', '0123456789')" + + # --- Nemeth Default Alphabet sans-serif-bold-italic chars. --- + + - "𝘼-𝙕": # 0x1d63c - 0x1d655 + - tc: "SBE" + - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝙖-𝙯": # 0x1d656 - 0x1d66f + - tc: "SBE" + - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝞐-𝞨": # 0x1d790 - 0x1d7a8 + - tc: "SBG" + - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ')" + + - "𝞩": [t: "SBGL⠫"] # 0x1d7a9 + + - "𝞪-𝞺": # 0x1d7aa - 0x1d7ba + - tc: "SBG" + - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺', 'αβγδεζηθικλμνξοπρ')" + + - "𝞻": [t: "SBVL⠎"] # 0x1d7bb + - "𝞼": [t: "SBGL⠎"] # 0x1d7bc + - "𝞽": [t: "SBGL⠞"] # 0x1d7bd + - "𝞾": [t: "SBGL⠥"] # 0x1d7be + - "𝞿": [t: "SBVL⠋"] # 0x1d7bf + + - "𝟀": [t: "SBGL⠯"] # 0x1d7c0 + - "𝟁": [t: "SBGL⠽"] # 0x1d7c1 + - "𝟂": [t: "SBGL⠺"] # 0x1d7c2 + - "𝟃": [t: "SBVL⠙"] # 0x1d7c3 + + - "𝟄": [t: "SBGL⠑"] # 0x1d7c4 + - "𝟅": [t: "SBVL⠹"] # 0x1d7c5 + + - "𝟆": [t: "SBGL⠅"] # 0x1d7c6 + - "𝟇": [t: "SBGL⠋"] # 0x1d7c7 + - "𝟈": [t: "SBGL⠗"] # 0x1d7c8 + - "𝟉": [t: "SBGL⠏"] # 0x1d7c9 + + # --- Nemeth Default Alphabet sans-serif-italic chars. --- + - "𝘈-𝘡": # 0x1d608 - 0x1d621 + - tc: "SE" + - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝘢-𝘻": # 0x1d622 - 0x1d63b + - tc: "SE" + - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" + + # --- Nemeth Default Alphabet script chars. --- + - "𝒜-𝒵": # 0x1d49c - 0x1d4b5 + - tc: "TE" + - spell: "translate('.', '𝒜𝒞𝒟𝒢𝒥𝒦𝒩𝒪𝒫𝒬𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'ACDGJKNOPQSTUVWXYZ')" + + - "ℬ": [t: "TECL⠃"] # 0x212c + - "ℰ": [t: "TECL⠑"] # 0x2130 + - "ℱ": [t: "TECL⠋"] # 0x2131 + - "ℋ": [t: "TECL⠓"] # 0x210b + - "ℐ": [t: "TECL⠊"] # 0x2110 + - "ℒ": [t: "TECL⠇"] # 0x2112 + - "ℳ": [t: "TECL⠍"] # 0x2133 + - "ℛ": [t: "TECL⠗"] # 0x211b + + - "𝒶-𝓏": # 0x1d4b6 - 0x1d4cf + - tc: "TE" + - spell: "translate('.', '𝒶𝒷𝒸𝒹𝒻𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏', 'abcdfhijklmnpqrstuvwxyz')" + + - "ℯ": [t: "TEL⠑"] # 0x212f + - "ℊ": [t: "TEL⠛"] # 0x210a + - "ℴ": [t: "TEL⠕"] # 0x2134 + + # --- Nemeth Default Alphabet squared chars. --- + + - "🄰": [t: "⠫⠲⠸⠫⠠⠁⠻"] # 0x1f130 + - "🄱": [t: "⠫⠲⠸⠫⠠⠃⠻"] # 0x1f131 + - "🄲": [t: "⠫⠲⠸⠫⠠⠉⠻"] # 0x1f132 + - "🄳": [t: "⠫⠲⠸⠫⠠⠙⠻"] # 0x1f133 + - "🄴": [t: "⠫⠲⠸⠫⠠⠑⠻"] # 0x1f134 + - "🄵": [t: "⠫⠲⠸⠫⠠⠋⠻"] # 0x1f135 + - "🄶": [t: "⠫⠲⠸⠫⠠⠛⠻"] # 0x1f136 + - "🄷": [t: "⠫⠲⠸⠫⠠⠓⠻"] # 0x1f137 + - "🄸": [t: "⠫⠲⠸⠫⠠⠊⠻"] # 0x1f138 + - "🄹": [t: "⠫⠲⠸⠫⠠⠚⠻"] # 0x1f139 + - "🄺": [t: "⠫⠲⠸⠫⠠⠅⠻"] # 0x1f13a + - "🄻": [t: "⠫⠲⠸⠫⠠⠇⠻"] # 0x1f13b + - "🄼": [t: "⠫⠲⠸⠫⠠⠍⠻"] # 0x1f13c + - "🄽": [t: "⠫⠲⠸⠫⠠⠝⠻"] # 0x1f13d + - "🄾": [t: "⠫⠲⠸⠫⠠⠕⠻"] # 0x1f13e + - "🄿": [t: "⠫⠲⠸⠫⠠⠏⠻"] # 0x1f13f + - "🅀": [t: "⠫⠲⠸⠫⠠⠟⠻"] # 0x1f140 + - "🅁": [t: "⠫⠲⠸⠫⠠⠗⠻"] # 0x1f141 + - "🅂": [t: "⠫⠲⠸⠫⠠⠎⠻"] # 0x1f142 + - "🅃": [t: "⠫⠲⠸⠫⠠⠞⠻"] # 0x1f143 + - "🅄": [t: "⠫⠲⠸⠫⠠⠥⠻"] # 0x1f144 + - "🅅": [t: "⠫⠲⠸⠫⠠⠧⠻"] # 0x1f145 + - "🅆": [t: "⠫⠲⠸⠫⠠⠺⠻"] # 0x1f146 + - "🅇": [t: "⠫⠲⠸⠫⠠⠭⠻"] # 0x1f147 + - "🅈": [t: "⠫⠲⠸⠫⠠⠽⠻"] # 0x1f148 + - "🅉": [t: "⠫⠲⠸⠫⠠⠵⠻"] # 0x1f149 + + # --- Nemeth Default Alphabet sub chars. --- + + - "₀": [t: "⠰⠴"] # 0x2080 + - "₁": [t: "⠰⠂"] # 0x2081 + - "₂": [t: "⠰⠆"] # 0x2082 + - "₃": [t: "⠰⠒"] # 0x2083 + - "₄": [t: "⠰⠲"] # 0x2084 + - "₅": [t: "⠰⠢"] # 0x2085 + - "₆": [t: "⠰⠖"] # 0x2086 + - "₇": [t: "⠰⠶"] # 0x2087 + - "₈": [t: "⠰⠦"] # 0x2088 + - "₉": [t: "⠰⠔"] # 0x2089 + + # --- Nemeth Default Alphabet super chars. --- + + - "⁰": [t: "⠘⠴"] # 0x2070 + - "¹": [t: "⠘⠂"] # 0xb9 + - "²": [t: "⠘⠆"] # 0xb2 + - "³": [t: "⠘⠒"] # 0xb3 + - "⁴": [t: "⠘⠲"] # 0x2074 + - "⁵": [t: "⠘⠢"] # 0x2075 + - "⁶": [t: "⠘⠖"] # 0x2076 + - "⁷": [t: "⠘⠶"] # 0x2077 + - "⁸": [t: "⠘⠦"] # 0x2078 + - "⁹": [t: "⠘⠔"] # 0x2079 + + # --- Nemeth Default Characters latin-lower-phonetic chars. --- + + - "ø": [t: "⠈⠕"] # 0xf8 + + # --- Nemeth Default Characters math_geometry chars. --- + + - "─": [t: "⠱"] # 0x2500 + - "│": [t: "⠳"] # 0x2502 + - "▰": [t: "⠫⠸⠛"] # 0x25b0 + - "▱": [t: "⠫⠛"] # 0x25b1 + - "▽": [t: "⠨⠫"] # 0x25bd + - "◆": [t: "⠫⠸⠙"] # 0x25c6 + - "◇": [t: "⠫⠙"] # 0x25c7 + - "◈": [t: "⠫⠸⠙"] # 0x25c8 + - "◉": [t: "⠫⠸⠉"] # 0x25c9 + - "◊": [t: "⠫⠙"] # 0x25ca + - "⬟": [t: "⠫⠸⠢"] # 0x2b1f + - "⬠": [t: "⠫⠢"] # 0x2b20 + - "⬡": [t: "⠫⠖"] # 0x2b21 + - "⬢": [t: "⠫⠸⠖"] # 0x2b22 + - "⬣": [t: "⠫⠸⠖"] # 0x2b23 + - "⬤": [t: "⠫⠸⠉"] # 0x2b24 + - "⬥": [t: "⠫⠸⠙"] # 0x2b25 + + # --- Nemeth Default Characters math_symbols chars. --- + + - "\"": [t: "⠄⠄"] # 0x22 + - "¡": [t: "⠖"] # 0xa1 + - "¢": [t: "⠈⠉"] # 0xa2 + - "£": [t: "⠈⠇"] # 0xa3 + - "¤": [t: "⠈⠫"] # 0xa4 + - "¥": [t: "⠈⠽"] # 0xa5 + - "§": [t: "⠈⠠⠎"] # 0xa7 + - "©": [t: "⠘⠉"] # 0xa9 (Copyright) + - "ª": [t: "⠸⠰⠁"] # 0xaa + - "«": [t: "⠐⠅⠈⠐⠅⠻"] # 0xab + - "®": [t: "⠘⠗"] # 0xae + - "´": [t: "⠈"] # 0xb4 + - "µ": [t: "⠨⠍"] # 0xb5 + - "¶": [t: "⠈⠠⠏"] # 0xb6 + - "¿": [t: "⠦"] # 0xbf + - "ʹ": [t: "⠄"] # 0x2b9 + - "ʺ": [t: "⠄⠄"] # 0x2ba + - "˙": [t: "⠡"] # 0x2d9 + - "˜": [t: "⠈⠱"] # 0x2dc + - "‐": [t: "⠤"] # 0x2010 + - "‑": [t: "⠤"] # 0x2011 + - "‒": [t: "⠤⠤"] # 0x2012 + - "–": [t: "⠤⠤"] # 0x2013 + - "‖": [t: "⠳⠳"] # 0x2016 + - "†": [t: "⠸⠻"] # 0x2020 + - "‡": [t: "⠸⠸⠻"] # 0x2021 + - "•": [t: "⠡"] # 0x2022 + - "․": [t: "⠄"] # 0x2024 + - "‥": [t: "⠄⠄"] # 0x2025 + - "′": [t: "⠄"] # 0x2032 + - "″": [t: "⠄⠄"] # 0x2033 + - "‴": [t: "⠄⠄."] # 0x2034 (Triple prime) + - "⁗": [t: "⠄⠄.."] # 0x2057 (Quadruple prime) + - "‼": [t: "⠖⠖"] # 0x203c + - "‾": [t: "⠱"] # 0x203e + - "⁇": [t: "⠹⠹"] # 0x2047 + - "⁈": [t: "⠹⠖"] # 0x2048 + - "⁉": [t: "⠖⠹"] # 0x2049 + - "∀": [t: "⠈⠯"] # 0x2200 + - "∃": [t: "⠈⠿"] # 0x2203 + - "∄": [t: "⠌⠈⠿"] # 0x2204 + - "∅": [t: "⠸⠴"] # 0x2205 + - "∈": [t: "⠈⠑"] # 0x2208 + - "∉": [t: "⠌⠈⠑"] # 0x2209 + - "∊": [t: "⠈⠑"] # 0x220a + - "∋": [t: "⠈⠢"] # 0x220b + - "∌": [t: "⠌⠈⠢"] # 0x220c + - "∍": [t: "⠈⠢"] # 0x220d + - "∎": [t: "⠸⠳"] # 0x220e + - "−": [t: "⠤"] # 0x2212 + - "∓": [t: "⠤⠬"] # 0x2213 + - "∕": [t: "⠸⠌"] # 0x2215 + - "∖": [t: "⠸⠡"] # 0x2216 + - "∗": [t: "⠈⠼"] # 0x2217 + - "∘": [t: "⠨⠡"] # 0x2218 + - "∝": [t: "⠸⠿"] # 0x221d + - "∞": [t: "⠠⠿"] # 0x221e + - "∟": [t: "⠫⠪⠨⠗⠻"] # 0x221f + - "∠": [t: "⠫⠪"] # 0x2220 + - "∢": [t: "⠫⠪⠸⠫⠫⠁⠻"] # 0x2222 + - "∣": [t: "⠳"] # 0x2223 + - "∤": [t: "⠌⠳"] # 0x2224 + - "∥": [t: "⠫⠇"] # 0x2225 + - "∦": [t: "⠌⠫⠇"] # 0x2226 + - "∧": [t: "⠈⠩"] # 0x2227 + - "∨": [t: "⠈⠬"] # 0x2228 + - "∩": [t: "⠨⠩"] # 0x2229 + - "∪": [t: "⠨⠬"] # 0x222a + - "∫": [t: "⠮"] # 0x222b + - "∬": [t: "⠮⠮"] # 0x222c + - "∭": [t: "⠮⠮⠮"] # 0x222d + - "∮": [t: "⠮⠈⠫⠉⠻"] # 0x222e + - "∲": [t: "⠮⠈⠫⠪⠢⠔⠻"] # 0x2232 + - "∳": [t: "⠮⠈⠫⠢⠔⠕⠻"] # 0x2233 + - "∴": [t: "⠠⠡"] # 0x2234 + - "∵": [t: "⠈⠌"] # 0x2235 + - "∶": [t: "⠐⠂"] # 0x2236 + - "∷": [t: "⠰⠆"] # 0x2237 + - "∸": [t: "⠨⠤"] # 0x2238 + - "∼": [t: "⠈⠱"] # 0x223c + - "≁": [t: "⠌⠈⠱"] # 0x2241 + - "≂": [t: "⠱⠈⠱"] # 0x2242 + - "≃": [t: "⠈⠱⠱"] # 0x2243 + - "≄": [t: "⠌⠈⠱⠱"] # 0x2244 + - "≅": [t: "⠈⠱⠨⠅"] # 0x2245 + - "≆": [t: "⠈⠱⠌⠨⠅"] # 0x2246 + - "≇": [t: "⠌⠈⠱⠨⠅"] # 0x2247 + - "≈": [t: "⠈⠱⠈⠱"] # 0x2248 + - "≉": [t: "⠌⠈⠱⠈⠱"] # 0x2249 + - "≊": [t: "⠈⠱⠈⠱⠱"] # 0x224a + - "≍": [t: "⠈⠣⠠⠣"] # 0x224d + - "≎": [t: "⠈⠣⠠⠣"] # 0x224e + - "≐": [t: "⠐⠨⠅⠣⠡⠻"] # 0x2250 + - "≑": [t: "⠐⠨⠅⠩⠡⠣⠡⠻"] # 0x2251 + - "≗": [t: "⠐⠨⠅⠣⠨⠡⠻"] # 0x2257 + - "≘": [t: "⠐⠨⠅⠣⠫⠁⠻"] # 0x2258 + - "≙": [t: "⠐⠨⠅⠣⠸⠣⠻"] # 0x2259 + - "≚": [t: "⠐⠨⠅⠣⠸⠩⠻"] # 0x225a + - "≛": [t: "⠐⠨⠅⠣⠫⠸⠎⠻"] # 0x225b + - "≜": [t: "⠐⠨⠅⠣⠫⠞⠻"] # 0x225c + - "≝": [t: "⠐⠨⠅⠣⠙⠑⠋⠻"] # 0x225d + - "≞": [t: "⠐⠨⠅⠣⠍⠻"] # 0x225e + - "≟": [t: "⠐⠨⠅⠣⠸⠦⠻"] # 0x225f + - "≠": [t: "⠌⠨⠅"] # 0x2260 + - "≢": [t: "⠌⠸⠇"] # 0x2262 + - "≤": [t: "⠐⠅⠱"] # 0x2264 + - "≥": [t: "⠨⠂⠱"] # 0x2265 + - "≦": [t: "⠐⠅⠨⠅"] # 0x2266 + - "≧": [t: "⠨⠂⠨⠅"] # 0x2267 + - "≨": [t: "⠐⠅⠌⠨⠅"] # 0x2268 + - "≩": [t: "⠨⠂⠌⠨⠅"] # 0x2269 + - "≪": [t: "⠐⠅⠈⠐⠅⠻"] # 0x226a + - "≫": [t: "⠨⠂⠈⠨⠂⠻"] # 0x226b + - "≭": [t: "⠌⠈⠣⠠⠣"] # 0x226d + - "≮": [t: "⠌⠐⠅"] # 0x226e + - "≯": [t: "⠌⠨⠂"] # 0x226f + - "≰": [t: "⠌⠐⠅⠱"] # 0x2270 + - "≱": [t: "⠌⠨⠂⠱"] # 0x2271 + - "≲": [t: "⠐⠅⠈⠱"] # 0x2272 + - "≳": [t: "⠨⠂⠈⠱"] # 0x2273 + - "≴": [t: "⠌⠐⠅⠈⠱"] # 0x2274 + - "≵": [t: "⠌⠨⠂⠈⠱"] # 0x2275 + - "≶": [t: "⠐⠅⠨⠂"] # 0x2276 + - "≷": [t: "⠨⠂⠐⠅"] # 0x2277 + - "≸": [t: "⠌⠐⠅⠨⠂"] # 0x2278 + - "≹": [t: "⠌⠨⠂⠐⠅"] # 0x2279 + - "≺": [t: "⠨⠐⠅"] # 0x227a + - "≻": [t: "⠨⠨⠂"] # 0x227b + - "≼": [t: "⠨⠐⠅⠱"] # 0x227c + - "≽": [t: "⠨⠨⠐⠱"] # 0x227d + - "≾": [t: "⠨⠐⠅⠈⠱"] # 0x227e + - "≿": [t: "⠨⠨⠂⠈⠱"] # 0x227f + - "⊀": [t: "⠌⠨⠐⠅"] # 0x2280 + - "⊁": [t: "⠌⠨⠐⠅"] # 0x2281 + - "⊂": [t: "⠸⠐⠅"] # 0x2282 + - "⊃": [t: "⠸⠨⠂"] # 0x2283 + - "⊄": [t: "⠌⠸⠐⠅"] # 0x2284 + - "⊅": [t: "⠌⠸⠨⠂"] # 0x2285 + - "⊆": [t: "⠸⠐⠅⠱"] # 0x2286 + - "⊇": [t: "⠸⠨⠂⠱"] # 0x2287 + - "⊈": [t: "⠌⠸⠐⠅⠱"] # 0x2288 + - "⊉": [t: "⠌⠸⠨⠂⠱"] # 0x2289 + - "⊊": [t: "⠸⠐⠅⠌⠨⠅"] # 0x228a + - "⊋": [t: "⠸⠨⠂⠌⠨⠅"] # 0x228b + - "⊥": [t: "⠫⠏"] # 0x22a5 + - "⋀": [t: "⠈⠩"] # 0x22c0 + - "⋁": [t: "⠈⠬"] # 0x22c1 + - "⋂": [t: "⠨⠩"] # 0x22c2 + - "⋃": [t: "⠨⠬"] # 0x22c3 + - "⋄": [t: "⠫⠙"] # 0x22c4 + - "⋜": [t: "⠱⠐⠅"] # 0x22dc + - "⋝": [t: "⠱⠨⠂"] # 0x22dd + - "⟂": [t: "⠫⠏"] # 0x27c2 + - "⦀": [t: "⠳⠳⠳"] # 0x2980 + - "⦶": [t: "⠫⠉⠸⠫⠳⠻"] # 0x29b6 + - "⦷": [t: "⠫⠉⠸⠫⠫⠇⠻"] # 0x29b7 + - "⦸": [t: "⠫⠉⠸⠫⠸⠡⠻"] # 0x29b8 + - "⦹": [t: "⠫⠉⠸⠫⠫⠏⠻"] # 0x29b9 + - "⦿": [t: "⠫⠉⠸⠫⠔⠔⠻"] # 0x29bf + - "⧀": [t: "⠫⠉⠸⠫⠐⠅⠻"] # 0x29c0 + - "⧁": [t: "⠫⠉⠸⠫⠨⠂⠻"] # 0x29c1 + - "⨀": [t: "⠫⠉⠸⠫⠡⠻"] # 0x2a00 + - "⨁": [t: "⠫⠉⠸⠫⠬⠻"] # 0x2a01 + - "⨂": [t: "⠫⠉⠸⠫⠈⠡⠻"] # 0x2a02 + - "⪯": [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: "⠈⠎"] # 0xfe69 + - "$": [t: "⠈⠎"] # 0xff04 + - "_": [t: "⠱"] # 0xff3f + - "~": [t: "⠈⠱"] # 0xff5e + - "¢": [t: "⠈⠉"] # 0xffe0 + - "£": [t: "⠈⠇"] # 0xffe1 + - " ̄": [t: "⠱"] # 0xffe3 + - "¥": [t: "⠈⠽"] # 0xffe5 + + # --- Nemeth Default Characters rest chars. --- + + - "ℵ": [t: "⠠⠠⠁"] # 0x2135 + - "ℶ": [t: "⠠⠠⠃"] # 0x2136 + - "ℷ": [t: "⠠⠠⠉"] # 0x2137 + - "ℸ": [t: "⠠⠠⠙"] # 0x2138 + - "à": [t: "⠈⠁"] # 0xe0 + - "á": [t: "⠈⠁"] # 0xe1 + - "â": [t: "⠈⠁"] # 0xe2 + - "ã": [t: "⠈⠁"] # 0xe3 + - "ä": [t: "⠈⠁"] # 0xe4 + - "å": [t: "⠈⠁"] # 0xe5 + - "ç": [t: "⠈⠉"] # 0xe7 + - "è": [t: "⠈⠑"] # 0xe8 + - "é": [t: "⠈⠑"] # 0xe9 + - "ê": [t: "⠈⠑"] # 0xea + - "ë": [t: "⠈⠑"] # 0xeb + - "ì": [t: "⠈⠊"] # 0xec + - "í": [t: "⠈⠊"] # 0xed + - "î": [t: "⠈⠊"] # 0xee + - "ï": [t: "⠈⠊"] # 0xef + - "ñ": [t: "⠈⠝"] # 0xf1 + - "ò": [t: "⠈⠕"] # 0xf2 + - "ó": [t: "⠈⠕"] # 0xf3 + - "ô": [t: "⠈⠕"] # 0xf4 + - "õ": [t: "⠈⠕"] # 0xf5 + - "ö": [t: "⠈⠕"] # 0xf6 + - "ù": [t: "⠈⠥"] # 0xf9 + - "ú": [t: "⠈⠥"] # 0xfa + - "û": [t: "⠈⠥"] # 0xfb + - "ü": [t: "⠈⠥"] # 0xfc + - "ý": [t: "⠈⠽"] # 0xfd + - "ÿ": [t: "⠈⠽"] # 0xff + + - "À": [t: "⠠⠈⠁"] # 0xc0 + - "Á": [t: "⠠⠈⠁"] # 0xc1 + - "Â": [t: "⠠⠈⠁"] # 0xc2 + - "Ã": [t: "⠠⠈⠁"] # 0xc3 + - "Ä": [t: "⠠⠈⠁"] # 0xc4 + - "Å": [t: "⠠⠈⠁"] # 0xc5 + - "Ç": [t: "⠠⠈⠉"] # 0xc7 + - "È": [t: "⠠⠈⠑"] # 0xc8 + - "É": [t: "⠠⠈⠑"] # 0xc9 + - "Ê": [t: "⠠⠈⠑"] # 0xca + - "Ë": [t: "⠠⠈⠑"] # 0xcb + - "Ì": [t: "⠠⠈⠊"] # 0xcc + - "Í": [t: "⠠⠈⠊"] # 0xcd + - "Î": [t: "⠠⠈⠊"] # 0xce + - "Ï": [t: "⠠⠈⠊"] # 0xcf + - "Ñ": [t: "⠠⠈⠝"] # 0xd1 + - "Ò": [t: "⠠⠈⠕"] # 0xd2 + - "Ó": [t: "⠠⠈⠕"] # 0xd3 + - "Ô": [t: "⠠⠈⠕"] # 0xd4 + - "Ö": [t: "⠠⠈⠕"] # 0xd6 + - "Ù": [t: "⠠⠈⠥"] # 0xd9 + - "Ú": [t: "⠠⠈⠥"] # 0xda + - "Û": [t: "⠠⠈⠥"] # 0xdb + - "Ü": [t: "⠠⠈⠥"] # 0xdc + - "Ý": [t: "⠠⠈⠽"] # 0xdd + - "←": [t: "⠫⠪⠒⠒"] # 0x2190 + + - "↑": # 0x2191 + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠣"] + else: [t: "⠫⠣⠒⠒⠕"] + + - "↓": # 0x2193 + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠩"] + else: [t: "⠫⠩⠒⠒⠕"] + + - "↔": [t: "⠫⠪⠒⠒⠕"] # 0x2194 + - "↕": [t: "⠫⠣⠩⠪⠒⠒⠕"] # 0x2195 + - "↖": [t: "⠫⠘⠪⠒⠒"] # 0x2196 + - "↗": [t: "⠫⠘⠒⠒⠕"] # 0x2197 + - "↘": [t: "⠫⠰⠒⠒⠕"] # 0x2198 + - "↙": [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: "⠫⠠⠳⠒⠒⠕"] # 0x21b4 + - "↵": [t: "⠫⠩⠠⠳⠒⠒⠕"] # 0x21b5 + - "↶": [t: "⠫⠢⠔⠕"] # 0x21b6 + - "↷": [t: "⠫⠪⠢⠔"] # 0x21b7 + - "↺": [t: "⠫⠢⠔⠕"] # 0x21ba + - "↻": [t: "⠫⠪⠢⠔"] # 0x21bb + - "⇄": [t: "⠫⠒⠒⠕⠫⠪⠒⠒"] # 0x21c4 + - "⇅": [t: "⠫⠣⠒⠒⠕⠐⠫⠩⠒⠒⠕"] # 0x21c5 + - "⇆": [t: "⠫⠪⠒⠒⠫⠒⠒⠕"] # 0x21c6 + - "⇇": [t: "⠫⠚⠒⠒⠫⠚⠒⠒"] # 0x21c7 + - "⇈": [t: "⠫⠣⠒⠒⠕⠐⠫⠣⠒⠒⠕"] # 0x21c8 + - "⇉": [t: "⠫⠒⠒⠕⠫⠒⠒⠕"] # 0x21c9 + - "⇊": [t: "⠫⠩⠒⠒⠕⠐⠫⠩⠒⠒⠕"] # 0x21ca + - "⇍": [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 FIX: how to represent triple shafts? + - "⇛": [t: "⠫⠶⠶⠕⠕"] # 0x21db FIX: how to represent triple shafts? + - "⇜": [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: "⠫⠣⠸⠪⠒⠒⠕"] # 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: "⠙"] # 0x2146 + - "ⅇ": [t: "⠑"] # 0x2147 + - "ⅈ": [t: "⠊"] # 0x2148 + - "⌜": [t: "⠈⠘⠷"] # 0x231c + - "⌝": [t: "⠈⠘⠾"] # 0x231d + - "⌞": [t: "⠈⠰⠷"] # 0x231e + - "⌟": [t: "⠈⠰⠾"] # 0x231f + - "〈": [t: "⠨⠨⠷"] # 0x2329 + - "〉": [t: "⠨⠨⠾"] # 0x232a + - "⏞": [t: "⠨⠷"] # 0x23de + - "⏟": [t: "⠨⠾"] # 0x23df + - "⎴": [t: "⠈⠷"] # 0x23b4 + - "⎵": [t: "⠈⠾"] # 0x23b5 + - "⟪": [t: "⠨⠨⠨⠷"] # 0x27ea + - "⟫": [t: "⠨⠨⠨⠾"] # 0x27eb + - "〈": [t: "⠨⠨⠷"] # 0x3008 + - "〉": [t: "⠨⠨⠾"] # 0x3009 + - "《": [t: "⠨⠨⠨⠷"] # 0x300a + - "》": [t: "⠨⠨⠨⠾"] # 0x300b + - "「": [t: "⠈⠘⠷"] # 0x300c + - "」": [t: "⠈⠘⠾"] # 0x300d + - "↼": [t: "⠫⠈⠪⠒⠒"] # 0x21bc + - "↽": [t: "⠫⠠⠪⠒⠒"] # 0x21bd + - "↾": [t: "⠫⠣⠒⠒⠠⠕"] # 0x21be + - "↿": [t: "⠫⠣⠒⠒⠈⠕"] # 0x21bf + - "⇀": [t: "⠫⠒⠒⠈⠕"] # 0x21c0 + - "⇁": [t: "⠫⠒⠒⠠⠕"] # 0x21c1 + - "⇂": [t: "⠫⠪⠒⠒⠈⠕"] # 0x21c2 + - "⇃": [t: "⠫⠩⠒⠒⠠⠕"] # 0x21c3 + - "⇋": # 0x21cb + + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠪⠒⠫⠒⠕"] + else: [t: "⠫⠈⠪⠒⠒⠫⠒⠒⠈⠕"] + - "⇌": # 0x21cc + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] + + - "℔": [t: "⠳"] # 0x2114 + - "™": [t: "⠘⠞"] # 0x2122 + - "Å": [t: "⠈⠠⠁"] # 0x212b + - "­": [t: "⠤"] # 0xad + - " -​": [t: "W"] # 0x2000 - 0x200b (various spaces / ZWSP) + - "
": [t: "W"] # 0x2028 + - "
": [t: "W"] # 0x2029 + - " ": [t: "W"] # 0x202f + - " ": [t: "W"] # 0x205f + - "₣": [t: "⠈⠋"] # 0x20A3 (French franc sign + - "₦": [t: "⠈⠝"] # 0x2046 (Naira sign) + - "₩": [t: "⠈⠺"] # 0x20A9 (Won sign) + - "€": [t: "⠈⠑"] # 0x20AC (Euro sign) + - "": [t: "W"] # 0xfeff + - "☆": [t: "⠫⠎"] # 0x2606 + - "✕": [t: "⠈⠡"] # 0x2715 + - "¼": [t: "⠹⠂⠌⠲⠼"] # 0xbc + - "½": [t: "⠹⠂⠌⠆⠼"] # 0xbd + - "¾": [t: "⠹⠒⠌⠲⠼"] # 0xbe + - "⅐": [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: "⠹⠴⠌⠒⠼"] # 0x2189 + - "㉈": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠂⠴⠻⠻"] # 0x3248 + - "㉉": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠆⠴⠻⠻"] # 0x3249 + - "㉊": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠒⠴⠻⠻"] # 0x324a + - "㉋": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠲⠴⠻⠻"] # 0x324b + - "㉌": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠢⠴⠻⠻"] # 0x324c + - "㉍": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠖⠴⠻⠻"] # 0x324d + - "㉎": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠶⠴⠻⠻"] # 0x324e + - "㉏": [t: "⠫⠸⠲⠸⠫⠫⠉⠸⠫⠼⠦⠴⠻⠻"] # 0x324f + + + - "‘": [t: "P⠠⠦"] # 0x2018 (Left single quotation mark) + - "’": [t: "P⠠⠴"] # 0x2019 (Right single quotation mark) + - "“": [t: "P⠦"] # 0x201C (Left double quotation mark) + - "”": [t: "P⠴"] # 0x201D (Right double quotation mark) + - "‰": [t: "`00"] + - "ℏ": [t: "⠈⠓"] # 0x210F (Planck constant over two pi) + - "ℓ": [t: "TE⠇"] # 0x2113 (Script small l (differs from 1d4c1: 4-56-123)) + - "℘": [tc: "TCL⠏"] # 0x2118 (Script Capital P) +# - "ℝ": [t: ""] # 0x211D (Double-struck capital R (no nemeth double struck)) +# - "ⅆ": [t: ""] # 0x2146 (Double-struck italic small d) +# - "ⅇ": [t: ""] # 0x2147 (Double-struck italic small e) +# - "⇚": [t: "⠀⠫⠪⠸⠸⠀"] # 0x21DA (Leftwards triple arrow) +# - "⇛": [t: "⠀⠫⠸⠸⠕⠀"] # 0x21DB (Rightwards triple arrow) + - "∔": [t: "⠐⠬⠣⠡⠻"] # 0x2214 (Dot plus) + - "√": [t: "⠜"] # 0x221A (Square root) + - "∛": [t: "⠣⠒⠜"] # 0x221B (Cube root) + - "∜": [t: "⠣⠲⠜"] # 0x221C (Fourth root) + - "∡": [t: "⠫⠪⠸⠫⠫⠁⠻"] # 0x2221 (Measured angle) + - "∹": [t: "⠤⠐⠂"] # 0x2239 (Excess) + - "∺": [t: "⠐⠤⠩⠡⠡⠣⠡⠡⠻"] # 0x223A (Geometric proportion) + - "∻": [t: "⠐⠈⠱⠩⠡⠣⠡⠻"] # 0x223B (Homothetic) + - "∽": [t: "⠠⠱"] # 0x223D (Reversed tilde) + - "≋": [t: "⠈⠱⠈⠱⠈⠱"] # 0x224B (Triple tilde) + - "≌": [t: "⠠⠱⠨⠅"] # 0x224C (All equal to) + - "≏": [t: "⠈⠣⠱"] # 0x224F (Difference between) + - "≔": [t: "⠐⠂⠨⠅"] # 0x2254 (Colon equals) + - "≕": [t: "⠨⠅⠐⠂"] # 0x2255 (Equals colon) + - "≖": [t: "⠀⠨⠡⠈⠨⠅⠻⠀"] # 0x2256 (Ring in equal to) + - "⊌": [t: "⠨⠬⠈⠫⠪⠒⠻"] # 0x228C (Multiset) + - "⊍": [t: "⠡⠈⠨⠬⠻"] # 0x228D (Multiset multiplication) + - "⊎": [t: "⠬⠈⠨⠬⠻"] # 0x228E (Multiset union) + - "⊕": [t: "⠫⠉⠸⠫⠬⠻"] # 0x2295 (Circled plus) + - "⊖": [t: "⠫⠉⠸⠫⠤⠻"] # 0x2296 (Circled minus) + - "⊗": [t: "⠫⠉⠸⠫⠈⠡⠻"] # 0x2297 (Circled times) + - "⊘": [t: "⠫⠉⠸⠫⠸⠌⠻"] # 0x2298 (Circled division slash) + - "⊙": [t: "⠫⠉⠸⠫⠡⠻"] # 0x2299 (Circled dot operator) + - "⊚": [t: "⠫⠉⠸⠫⠘⠨⠡⠻"] # 0x229a (Circled ring operator) + - "⊛": [t: "⠫⠉⠸⠫⠈⠼⠻"] # 0x229B (Circled asterisk operator) + - "⊜": [t: "⠫⠉⠸⠫⠨⠅⠻"] # 0x229C (Circled equals) + - "⊝": [t: "⠫⠉⠸⠫⠤⠤⠻"] # 0x229D (Circled dash) + - "⊞": [t: "⠫⠲⠸⠫⠬⠻"] # 0x229E (Squared plus) + - "⊟": [t: "⠫⠲⠸⠫⠤⠻"] # 0x229F (Squared minus) + - "⊠": [t: "⠫⠲⠸⠫⠈⠡⠻"] # 0x22A0 (Squared times) + - "⊡": [t: "⠫⠲⠸⠫⠡⠻"] # 0x22A1 (Squared dot operator) + - "⊢": [t: "⠫⠳⠒"] # 0x22A2 (Right tack) + - "⊣": [t: "⠫⠒⠳"] # 0x22A3 (Left tack) + - "⊤": [t: "⠫⠩⠳⠒"] # 0x22A4 (Down tack) +# - "⊥": [t: "⠀⠫⠏⠀ or ⠀⠫⠣⠳⠒⠀"] # 0x22A5 (Up tack (See also 27C2)) +# - "⊦": [t: ""] # 0x22A6 (Assertion) +# - "⊨": [t: ""] # 0x22A8 (TRUE) +# - "⊲": [t: ""] # 0x22B2 (Normal subgroup of) +# - "⊳": [t: ""] # 0x22B3 (Contains as normal subgroup) +# - "⊴": [t: ""] # 0x22B4 (Normal subgroup of or equal to) +# - "⊵": [t: ""] # 0x22B5 (Contains as normal subgroup or equal to) + - "⊶": [t: "⠫⠨⠡⠒⠡"] # 0x22B6 (Original of) + - "⊷": [t: "⠫⠡⠒⠨⠡"] # 0x22B7 (Image of) + - "⊸": [t: "⠫⠒⠨⠡"] # 0x22B8 (Multimap) +# - "⊾": [t: ""] # 0x22BE (Right angle w arc) + - "⊿": [t: "⠫⠞⠨⠗⠻"] # 0x22BF (Right triangle) + - "⋅": [t: "⠡"] # 0x22C5 (Dot operator) + - "∙": [t: "⠡"] # 0x2219 -- similar looking to 0x22c5, so using the same braille + - "⋆": [t: "⠫⠎"] # 0x22C6 (Star operator) + - "⋍": [t: "⠠⠱⠱"] # 0x22CD (Reversed tilde equals) + - "⋖": [t: "⠡⠈⠐⠅⠻"] # 0x22D6 (Less-than w dot) + - "⋗": [t: "⠡⠈⠨⠂⠻"] # 0x22D7 (Greater-than w dot) + - "⋘": [t: "⠐⠅⠈⠐⠅⠈⠐⠅⠻"] # 0x22D8 (Much less-than) + - "⋙": [t: "⠨⠂⠈⠨⠂⠈⠨⠂⠻"] # 0x22D9 (Much greater-than) + - "⋲": [t: "⠱⠈⠈⠑⠻"] # 0x22F2 (Element of w long horizontal stroke) + - "⋵": [t: "⠐⠈⠑⠣⠡⠻"] # 0x22F5 (Element of w dot above) + - "⋶": [t: "⠱⠈⠑"] # 0x22F6 (Element of w overbar) + - "⋺": [t: "⠱⠈⠈⠢⠻"] # 0x22FA (Contains w long horizontal stroke) + - "⋽": [t: "⠱⠈⠢"] # 0x22FD (Contains w overbar) + - "⌈": [t: "⠈⠘⠷"] # 0x2308 (Left ceiling) + - "⌉": [t: "⠈⠘⠾"] # 0x2309 (Right ceiling) + - "⌊": [t: "⠈⠰⠷"] # 0x230A (Left floor) + - "⌋": [t: "⠈⠰⠾"] # 0x230B (Right floor) + - "⌢": [t: "⠫⠁"] # 0x2322 (Frown) + - "⌣": [t: "⠫⠄"] # 0x2323 (Smile) + - "■": [t: "⠫⠸⠲"] # 0x25A0 (Filled square) + - "▫": [t: "⠫⠲"] # 0x25AB (Small white Square) + - "□": [t: "⠫⠲"] # 0x25A1 (Square) + - "◻": [t: "⠫⠲"] # 0x25FB (Square) + - "▬": [t: "⠫⠸⠗"] # 0x25AC (Filled rectangle) + - "▭": [t: "⠫⠗"] # 0x25AD (Rectangle) + - "▲": [t: "⠫⠸⠞"] # 0x25B2 (Black up-pointing triangle) + - "△": [t: "⠫⠞"] # 0x25B3 (White up-pointing triangle) + - "▼": [t: "⠸⠨⠫"] # 0x25BC (Black down-pointing triangle) + - "○": [t: "⠫⠉"] # 0x25CB (White circle) + - "●": [t: "⠫⠸⠉"] # 0x25CF (Black circle) + - "◫": [t: "⠫⠲⠸⠫⠳⠻"] # 0x25EB (White square w vertical bisecting line) +# - "⊥": [t: "⠫⠏"] # 0x27C2 (Perpendicular) + - "⟃": [t: "⠨⠡⠈⠸⠐⠅⠻"] # 0x27C3 (Open subset) + - "⟄": [t: "⠨⠡⠈⠸⠨⠂⠻"] # 0x27C4 (Open superset) + - "⟜": [t: "⠫⠨⠡⠒⠒"] # 0x27DC (Left multimap) + - "⟝": [t: "⠫⠳⠒⠒"] # 0x27DD (Long right tack) + - "⟞": [t: "⠫⠒⠒⠳"] # 0x27DE (Long left tack) + - "⟟": [t: "⠫⠣⠳⠒⠒⠨⠡"] # 0x27DF (Up tack w circle above) + - "⟦": [t: "⠈⠸⠷"] # 0x27E6 (Mathematical left white square bracket) + - "⟧": [t: "⠈⠸⠾"] # 0x27E7 (Mathematical right white square bracket) + - "⟨": [t: "⠨⠨⠷"] # 0x27E8 (Mathematical left angle bracket) + - "⟩": [t: "⠨⠨⠾"] # 0x27E9 (Mathematical right angle bracket) + - "⟵": [t: "⠀⠫⠪⠒⠒⠒⠀"] # 0x27F5 (Long leftwards arrow ) + - "⟶": [t: "⠀⠫⠒⠒⠒⠕⠀"] # 0x27F6 (Long rightwards arrow) + - "⟷": [t: "⠀⠫⠪⠒⠒⠒⠕⠀"] # 0x27F7 (Long left right arrow) + - "⟸": [t: "⠀⠫⠪⠶⠶⠶⠀"] # 0x27F8 (Long leftwards arrow ) + - "⟹": [t: "⠀⠫⠶⠶⠶⠕⠀"] # 0x27F9 (Long rightwards arrow) + - "⟺": [t: "⠀⠫⠪⠶⠶⠶⠕⠀"] # 0x27FA (Long left right arrow) + - "⟻": [t: "⠀⠫⠪⠒⠒⠒⠳⠀"] # 0x27FB (Long leftwards arrow from bar) + - "⟼": [t: "⠀⠫⠳⠒⠒⠒⠕⠀"] # 0x27FC (Long rightwards arrow from bar) + - "⟽": [t: "⠀⠫⠪⠶⠶⠶⠳⠀"] # 0x27FD (Long leftwards double arrow from bar) + - "⟾": [t: "⠀⠫⠳⠶⠶⠶⠕⠀"] # 0x27FE (Long rightwards double arrow from bar) + - "⟿": [t: "⠀⠫⠢⠤⠔⠒⠢⠤⠔⠒⠢⠕⠀"] # 0x27FF (Long rightwards squiggle arrow) + - "⤡": [t: "⠀⠫⠘⠪⠒⠒⠕⠀"] # 0x2921 (North west and south east arrow) + - "⤢": [t: "⠀⠫⠰⠪⠒⠒⠕⠀"] # 0x2922 (North east and south west arrow) + - "🣐": # 0x1f8d0 (Long rightwards arrow over leftwards arrow (equilibrium)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠠⠪⠒⠒"] + - "🣑": # 0x1f8d1 (Long rightwards harpoon over leftwards harpoon (equilibrium)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠠⠪⠒⠒"] + - "🣒": # 0x1f8d2 (Long rightwards harpoon over short leftwards harpoon (equilibrium)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] + - "🣓": # 0x1f8d3 (Short rightwards harpoon over long leftwards harpoon (equilibrium, trend to the right)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒⠒"] + else: [t: "⠫⠒⠈⠕⠫⠠⠪⠒⠒"] + - "🣔": # 0x1f8d4 (Long leftwards harpoon over short rightwards harpoon (equilibrium, trend to the left)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠪⠒⠒⠫⠒⠕"] + else: [t: "⠫⠈⠪⠒⠒⠫⠒⠠⠕"] + - "🣕": # 0x1f8d5 (Short leftwards harpoon over long rightwards harpoon (equilibrium, trend to the left)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠪⠒⠫⠒⠒⠕"] + else: [t: "⠫⠈⠪⠒⠫⠒⠒⠠⠕"] + - "⥂": # 0x2942 (Long rightwards harpoon over short leftwards harpoon (equilibrium)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠒⠕⠫⠪⠒"] + else: [t: "⠫⠒⠒⠈⠕⠫⠈⠪⠒⠒"] + - "⥄": # 0x2944 (Short rightwards harpoon over long leftwards harpoon (equilibrium, trend to the right)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠒⠕⠫⠪⠒⠒"] + else: [t: "⠫⠒⠈⠕⠫⠠⠪⠒⠒"] + - "⥃": # 0x2943 (Long leftwards harpoon over short rightwards harpoon (equilibrium, trend to the left)) + - test: + if: "@data-chem-equation-op or ancestor-or-self::*[contains(@intent, ':chemical-equation')]" + then: [t: "⠫⠪⠒⠒⠫⠒⠕"] + else: [t: "⠫⠈⠪⠒⠒⠫⠒⠠⠕"] + - "🣖": [t: "⠌⠫⠒⠒⠕"] # 0x1f8d6 (long rightwards arrow with through X) + - "🣗": [t: "⠌⠫⠒⠒⠕"] # 0x1f8d7 (long rightwards arrow with double slash) chem code says it is same as 0x1f8d6 + + + - "⦃": [t: "⠨⠸⠷"] # 0x2983 (Left white curly bracket) + - "⦄": [t: "⠨⠸⠾"] # 0x2984 (Right white curly bracket) + - "⦑": [t: "⠡⠈⠨⠨⠷⠻"] # 0x2991 (Left angle bracket w dot) + - "⦒": [t: "⠡⠈⠨⠨⠾⠻"] # 0x2992 (Right angle bracket w dot) + - "⧄": [t: "⠫⠲⠸⠫⠔⠻"] # 0x29C4 (Square rising diagonal slash) + - "⧅": [t: "⠫⠲⠸⠫⠢⠻"] # 0x29C5 (Square falling diagonal slash) + - "⧆": [t: "⠫⠲⠸⠫⠈⠼⠻"] # 0x29C6 (Squared asterisk) + - "⧊": [t: "⠐⠫⠞⠣⠡⠻"] # 0x29CA (Triangle w dot above) + - "⧋": [t: "⠫⠞⠱"] # 0x29CB (Triangle w underbar) + - "⧌": [t: "⠫⠞⠎"] # 0x29CC (s in triangle §114) + - "⧦": [t: "⠫⠳⠶⠶⠳"] # 0x29E6 (Gleich stark) + - "⨌": [t: "⠮⠮⠮⠮"] # 0x2A0C (Quadruple integral operator) + - "⨍": [t: "⠮⠈⠱⠻"] # 0x2A0D (Finite part integral) + - "⨎": [t: "⠮⠈⠱⠱⠻"] # 0x2A0E (Integral w double stroke) + - "⨖": [t: "⠮⠈⠫⠲⠻"] # 0x2A16 (Quaternion integral operator) + - "⨘": [t: "⠮⠈⠈⠡⠻"] # 0x2A18 (Integral w times sign) + - "⨙": [t: "⠮⠈⠨⠩⠻"] # 0x2A19 (Integral w intersection) + - "⨚": [t: "⠮⠈⠨⠬⠻"] # 0x2A1A (Integral w union) + - "⨛": [t: "⠣⠮"] # 0x2A1B (Integral w overbar (upper)) + - "⨜": [t: "⠩⠮"] # 0x2A1C (Integral w underbar (lower)) + - "⨢": [t: "⠐⠬⠣⠨⠡⠻"] # 0x2A22 (Plus sign w small circle above) + - "⨣": [t: "⠐⠬⠣⠸⠣⠻"] # 0x2A23 (Plus sign w circumflex accent above) + - "⨤": [t: "⠐⠬⠣⠈⠱⠻"] # 0x2A24 (Plus sign w tilde above) + - "⨥": [t: "⠐⠬⠩⠡⠻"] # 0x2A25 (Plus sign w dot below) + - "⨪": [t: "⠐⠱⠩⠡⠻"] # 0x2A2A (Minus sign w dot below) + - "⨰": [t: "⠐⠈⠡⠣⠡⠻"] # 0x2A30 (Multiplication sign w dot above) + - "⨱": [t: "⠈⠡⠱"] # 0x2A31 (Multiplication sign w underbar) + - "⨸": [t: "⠫⠉⠸⠫⠨⠌⠻"] # 0x2A38 (Circled division sign) + - "⨹": [t: "⠫⠞⠸⠫⠬⠻"] # 0x2A39 (Plus sign in triangle) + - "⨺": [t: "⠫⠞⠸⠫⠤⠻"] # 0x2A3A (Minus sign in triangle) + - "⨻": [t: "⠫⠞⠸⠫⠈⠡⠻"] # 0x2A3B (Multiplication sign in triangle) + - "⩀": [t: "⠡⠈⠨⠩⠻"] # 0x2A40 (Intersection w dot) + - "⩁": [t: "⠤⠈⠨⠬⠻"] # 0x2A41 (Union w minus sign) + - "⩂": [t: "⠱⠨⠬"] # 0x2A42 (Union w overbar) + - "⩃": [t: "⠱⠨⠩"] # 0x2A43 (Intersection w overbar) + - "⩑": [t: "⠐⠈⠩⠣⠡⠻"] # 0x2A51 (Logical AND w dot above) + - "⩒": [t: "⠐⠈⠬⠣⠡⠻"] # 0x2A52 (Logical OR w dot above) + - "⩜": [t: "⠱⠈⠈⠩⠻"] # 0x2A5C (Logical AND with horizontal dash) + - "⩝": [t: "⠱⠈⠈⠬⠻"] # 0x2A5D (Logical OR with horizontal dash) + - "⩞": [t: "⠱⠱⠈⠩"] # 0x2A5E (Logical AND w double overbar) + - "⩟": [t: "⠈⠩⠱"] # 0x2A5F (Logical AND w underbar) + - "⩠": [t: "⠈⠩⠱⠱"] # 0x2A60 (Logical AND w double underbar) + - "⩢": [t: "⠱⠱⠈⠬"] # 0x2A62 (Logical OR w double overbar) + - "⩣": [t: "⠈⠬⠱⠱"] # 0x2A63 (Logical OR w double underbar) + - "⩦": [t: "⠐⠨⠅⠩⠡⠻"] # 0x2A66 (Equals sign w dot below) + - "⩧": [t: "⠐⠸⠇⠣⠡⠻"] # 0x2A67 (Identical w dot above) + - "⩪": [t: "⠐⠈⠱⠣⠡⠻"] # 0x2A6A (Tilde operator w dot above) + - "⩬": [t: "⠈⠱⠱⠈⠱"] # 0x2A6C (Similar minus similar) + - "⩭": [t: "⠐⠈⠱⠨⠅⠣⠡⠻"] # 0x2A6D (Congruent w dot above) + - "⩮": [t: "⠐⠨⠅⠣⠈⠼⠻"] # 0x2A6E (Equals w asterisk) + - "⩯": [t: "⠐⠈⠱⠈⠱⠣⠸⠣⠻"] # 0x2A6F (Almost equal to w circumflex) + - "⩰": [t: "⠈⠱⠈⠱⠨⠅"] # 0x2A70 (Approximately equal or equal to) + - "⩱": [t: "⠨⠅⠬"] # 0x2A71 (Equals sign above plus sign) + - "⩲": [t: "⠬⠨⠅"] # 0x2A72 (Plus sign above equals sign) + - "⩳": [t: "⠨⠅⠈⠱"] # 0x2A73 (Equals sign above tilde operator) + - "⩴": [t: "⠐⠂⠐⠂⠨⠅"] # 0x2A74 (Double colon equal) + - "⩵": [t: "⠨⠅⠨⠅"] # 0x2A75 (Two consecutive equals signs) + - "⩶": [t: "⠨⠅⠨⠅⠨⠅"] # 0x2A76 (Three consecutive equals signs) + - "⩷": [t: "⠐⠨⠅⠩⠡⠡⠣⠡⠡⠻"] # 0x2A77 (Equals sign w two dots above and two dots below) + - "⩸": [t: "⠐⠸⠇⠣⠡⠡⠡⠡⠻"] # 0x2A78 (Equivalent w four dots above) + - "⩹": [t: "⠐⠅⠈⠨⠡⠻"] # 0x2A79 (Less than w circle inside) + - "⩺": [t: "⠨⠂⠈⠨⠡⠻"] # 0x2A7A (Greater than w circle inside) + - "⩻": [t: "⠐⠐⠅⠣⠸⠦⠻"] # 0x2A7B (Less-than w question mark above) + - "⩼": [t: "⠐⠨⠂⠣⠸⠦⠻"] # 0x2A7C (Greater-than w question mark above) + - "⪅": [t: "⠐⠅⠈⠱⠈⠱"] # 0x2A85 (Less-than or approximate) + - "⪆": [t: "⠨⠂⠈⠱⠈⠱"] # 0x2A86 (Greater-than or approximate) + - "⪋": [t: "⠐⠅⠨⠅⠨⠂"] # 0x2A8B (Less-than above double-line equal above greater-than) + - "⪌": [t: "⠨⠂⠨⠅⠐⠅"] # 0x2A8C (Greater-than above double-line equal above less-than) + - "⪍": [t: "⠐⠅⠈⠱⠱"] # 0x2A8D (Less-than above similar or equal to) + - "⪎": [t: "⠨⠂⠈⠱⠱"] # 0x2A8E (Greater-than above similar or equal to) + - "⪏": [t: "⠐⠅⠈⠱⠨⠂"] # 0x2A8F (Less-than above similar above greater-than) + - "⪐": [t: "⠨⠂⠈⠱⠐⠅"] # 0x2A90 (Greater-than above similar above less-than) + - "⪑": [t: "⠐⠅⠨⠂⠨⠅"] # 0x2A91 (Less-than above greater-than above double-line equal) + - "⪒": [t: "⠨⠂⠐⠅⠨⠅"] # 0x2A92 (Greater-than above less-than above double-line equal) + - "⪙": [t: "⠨⠅⠐⠅"] # 0x2A99 (Double-line equal to or less-than) + - "⪚": [t: "⠨⠅⠨⠂"] # 0x2A9A (Double-line equal to or greater-than) + - "⪝": [t: "⠈⠱⠐⠅"] # 0x2A9D (Similar or less-than) + - "⪞": [t: "⠈⠱⠨⠂"] # 0x2A9E (Similar or greater-than) + - "⪟": [t: "⠈⠱⠐⠅⠨⠅"] # 0x2A9F (Similar above less-than above equals sign) + - "⪠": [t: "⠈⠱⠨⠂⠨⠅"] # 0x2AA0 (Similar above greater-than above equals sign) + - "⪮": [t: "⠈⠣⠨⠅"] # 0x2AAE (Equals sign with bumpy above) + - "⪽": [t: "⠡⠈⠸⠐⠅⠻"] # 0x2ABD (Subset w dot) + - "⪾": [t: "⠡⠈⠸⠨⠂⠻"] # 0x2ABE (Superset w dot) + - "⪿": [t: "⠐⠸⠐⠅⠩⠬⠻"] # 0x2ABF (Subset with plus sign below) + - "⫀": [t: "⠐⠸⠨⠂⠩⠬⠻"] # 0x2AC0 (Superset with plus sign below) + - "⫁": [t: "⠐⠸⠐⠅⠩⠈⠡⠻"] # 0x2AC1 (Subset with multiplication sign below) + - "⫂": [t: "⠐⠸⠨⠂⠩⠈⠡⠻"] # 0x2AC2 (Superset with multiplication sign below) + - "⫅": [t: "⠸⠐⠅⠨⠅"] # 0x2AC5 (Subset of above equals sign) + - "⫆": [t: "⠸⠨⠂⠨⠅"] # 0x2AC6 (Superset of above equals sign) + - "⫇": [t: "⠸⠐⠅⠈⠱"] # 0x2AC7 (Subset of above tilde operator) + - "⫈": [t: "⠸⠨⠂⠈⠱"] # 0x2AC8 (Superset of above tilde operator) + - "⫉": [t: "⠸⠐⠅⠈⠱⠈⠱"] # 0x2AC9 (Subset of above almost equal to) + - "⫊": [t: "⠸⠨⠂⠈⠱⠈⠱"] # 0x2ACA (Superset of above almost equal to) + - "⫋": [t: "⠸⠐⠅⠌⠨⠅"] # 0x2ACB (Subset of above not equals sign) + - "⫌": [t: "⠸⠨⠂⠌⠨⠅"] # 0x2ACC (Superset of above not equals sign) + - "⫓": [t: "⠸⠐⠅⠸⠨⠂"] # 0x2AD3 (Subset above superset) + - "⫔": [t: "⠸⠨⠂⠸⠐⠅"] # 0x2AD4 (Superset above subset) + - "⫕": [t: "⠸⠐⠅⠸⠐⠅"] # 0x2AD5 (Subset above subset) + - "⫖": [t: "⠸⠨⠂⠸⠨⠂"] # 0x2AD6 (Superset above superset) + - "⫗": [t: "⠸⠨⠂⠐⠸⠐⠅"] # 0x2AD7 (Superset beside subset) + - "⫯": [t: "⠫⠩⠨⠡⠒⠒"] # 0x2AEF (Vertical line w circle above) + - "⫰": [t: "⠫⠣⠨⠡⠒⠒"] # 0x2AF0 (Vertical line w circle below) + - "⫱": [t: "⠫⠣⠨⠡⠒⠒⠳"] # 0x2AF1 (Down tack w circle below) + - "⫲": [t: "⠱⠈⠫⠇⠻"] # 0x2AF2 (Parallel w horizontal stroke) + - "⫳": [t: "⠈⠱⠈⠫⠇⠻"] # 0x2AF3 (Parallel w tilde operator) + - "⫴": [t: "⠳⠳⠳"] # 0x2AF4 (Triple vertical bar binary relation) + - "⬬": [t: "⠫⠸⠑"] # 0x2B2C (Black ellipse) + - "⬭": [t: "⠫⠑"] # 0x2B2D (White ellipse) + - "⬰": [t: "⠨⠡⠈⠫⠪⠒⠒⠻"] # 0x2B30 (Left arrow w small circle) + - "⬱": [t: "⠫⠪⠒⠒⠫⠪⠒⠒⠫⠪⠒⠒"] # 0x2B31 (Three leftwards arrows) + - "⬲": [t: "⠫⠪⠒⠒⠈⠫⠉⠸⠫⠬⠻"] # 0x2B32 (Left arrow w circled plus) + - "⮖": [t: "⠨⠡"] # 0x2B96 (Standard State Symbol) + - "⯃": [t: "⠫⠸⠦"] # 0x2BC3 (Horizontal black octagon) + + # SRE lists this as a custom transcription + - "⋊": [t: "⠈⠡⠳"] # 0x22ca (RIGHT NORMAL FACTOR SEMIDIRECT PRODUCT) diff --git a/Rules/Braille/UEB/UEB_Rules.yaml b/Rules/Braille/UEB/UEB_Rules.yaml index 912e03d8a..c161530dd 100644 --- a/Rules/Braille/UEB/UEB_Rules.yaml +++ b/Rules/Braille/UEB/UEB_Rules.yaml @@ -8,7 +8,7 @@ variables: [MatchingWhitespace: "true()"] replace: - test: - - if: "(@class='MathML-unit' or BaseNode(.)[@class='MathML-unit']) and @data-previous-space-width >= 0.25" # BANA 5.3(a) + - if: "(@class='MathML-unit' or contains(@data-intent-property, ':unit:') or BaseNode(.)[@class='MathML-unit' or contains(@data-intent-property, ':unit:')]) and @data-previous-space-width >= 0.25" # treat spaced units as separate exprs for G1 placement then: [t: "𝐖"] - else_if: "@data-previous-space-width > 1.1" then: [t: "⠬"] @@ -16,7 +16,7 @@ then: [t: "W"] - x: "." - test: - - if: "(@class='MathML-unit' or BaseNode(.)[@class='MathML-unit']) and @data-following-space-width >= 0.25" # BANA 5.3(a) + - if: "(@class='MathML-unit' or contains(@data-intent-property, ':unit:') or BaseNode(.)[@class='MathML-unit' or contains(@data-intent-property, ':unit:')]) and @data-following-space-width >= 0.25" # treat spaced units as separate exprs for G1 placement then: [t: "𝐖"] - else_if: "@data-following-space-width > 1.1" then: [t: "⠬"] @@ -107,7 +107,7 @@ replace: - t: "1⠷" - x: "*[1]" - - t: "1⠨⠌" + - t: "⠨⠌" - x: "*[2]" - t: "1⠾" @@ -256,9 +256,10 @@ tag: mo # add spaces around comparison operators unless they are in a script position, variables: # ratio not spaced in UEB; also make sure it is an infix operator (e.g., "~" can be prefix) + # ∴ / ∵ / ∎ are spaced like comparison signs (GTM 1.7.9 "∴ force = …"; GTM 11) - AddSpaces: "$UseSpacesAroundAllOperators or ( parent::*[self::m:mrow] and $NewScriptContext='' and - .!='∶' and IsInDefinition(., 'Braille', 'NemethComparisonOperators') )" + .!='∶' and (IsInDefinition(., 'Braille', 'NemethComparisonOperators') or .='∴' or .='∵' or .='∎') )" match: "." replace: - test: diff --git a/Rules/prefs.yaml b/Rules/prefs.yaml index 8a324721e..6c36c4020 100644 --- a/Rules/prefs.yaml +++ b/Rules/prefs.yaml @@ -61,6 +61,7 @@ UEB: StartMode: "Grade2" # Grade1/Grade2 -- assumed starting mode UEB braille (Grade1 assumes we are in G1 passage mode) + # Grade 1 indicators follow ICEB GTM 1.7 (also BANA 2026 math/science guidance) # UEB Guide to Technical Material (https://iceb.org/Guidelines_for_Technical_Material_2008-10.pdf) # says to normally treat Fraktur and DoubleStruck as Script diff --git a/docs/users.md b/docs/users.md index 6328e522f..a5ab487d3 100644 --- a/docs/users.md +++ b/docs/users.md @@ -10,8 +10,6 @@ MathCAT is a tool used together with a screen reader. Through MathCAT, you can h In screen readers NVDA and JAWS, MathCAT comes built-in. This means you don’t have to install it as an addon. -MathCAT is still available as an addon to NVDA. You can use it if you want access to the very latest features between NVDA releases. [How to use MathCAT as an addon is described here](#how-to-install-mathcat-as-an-addon-in-nvda). - ## Get started with MathCAT Use your screen reader on a web page or in an e-book as usual. When you get to a mathematical expression, the screen reader will read it out automatically. If you want to investigate the expression more closely you can activate the navigation mode by pressing Space. In NVDA, the NVDA-key+Alt+M can also be used. If you want to leave the navigation mode, press Escape. @@ -139,42 +137,4 @@ Note: tabular math means mathematical content in a table structure such as a mat ## Do you have feedback on MathCAT? -MathCAT is under active development, and we want your feedback. Do you have a feature request, or have you found any bugs? Please create an issue on [MathCAT's GitHub page](https://github.com/daisy/MathCAT/issues). You are also welcome to get in touch by signing up to [the mailing list of the DAISY MathCAT working group](https://www.surveymonkey.com/r/HXTLXGN). - -## How to install MathCAT as an addon in NVDA - -To use MathCAT as an add-on, do the following: - -1. Open the NVDA menu. -2. Choose Tools and then Add-on store. -3. In the add-on store, choose Available add-ons. - - The channel settings have the following options: - - Stable: Will only search for stable add-ons - - All: Will search for all add-ons - - Beta: Will search for add-ons that are in beta - - Dev: Will search for add-ons in development - - Choose option "All". - -4. Search for MathCAT and install. - -5. In the settings menu, you can control how add-ons are updated: - - - Open the NVDA menu - - Choose settings and Add-on Store - - For Automatic updates, you can choose between: - - Notify - - Update Automatically - - Deactivated - - For Default update channel, you have the following options: - - - Same: If automatic updates is set to “Update Automatically” and your add-on is a beta, it will update to the first available beta. - - Any: It will update to any version of the add-on. - - Do not update - - Stable: It will only update to stable versions. - - Beta or dev: It will update to beta or dev versions. - - Beta: It will update to beta versions. - - Dev: It will update to dev versions. \ No newline at end of file +MathCAT is under active development, and we want your feedback. Do you have a feature request, or have you found any bugs? Please create an issue on [MathCAT's GitHub page](https://github.com/daisy/MathCAT/issues). You are also welcome to get in touch by signing up to [the mailing list of the DAISY MathCAT working group](https://www.surveymonkey.com/r/HXTLXGN). \ No newline at end of file diff --git a/src/braille.rs b/src/braille.rs index 1966f09c0..4f0ac0ec7 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -15,6 +15,7 @@ use crate::canonicalize::get_parent; use std::borrow::Cow; use std::ops::Range; use std::sync::LazyLock; +#[allow(unused_imports)] use log::{debug, error}; fn is_ueb_prefix(ch: char) -> bool { @@ -1047,11 +1048,9 @@ fn ueb_cleanup(pref_manager: Ref, raw_braille: String) -> Str let use_only_grade1 = pref_manager.pref_to_string("UEB_START_MODE").as_str() == "Grade1"; - // '𝐖' is a hard break -- basically, it separates exprs - let mut result = result.split('𝐖') - .map(|str| pick_start_mode(str, use_only_grade1) + "W") - .collect::(); - result.pop(); // we added a 'W' at the end that needs to be removed. + // 𝐖 is a unit hard-break. For GTM 1.7 it is still a symbols-sequence boundary (same as W), + // so G1 passage decisions (1.7.3(b) / 1.7.5(c)) see the whole expression. + let result = pick_start_mode(&result, use_only_grade1); let result = result.replace("tW", "W"); @@ -1067,42 +1066,42 @@ fn ueb_cleanup(pref_manager: Ref, raw_braille: String) -> Str return result.to_string(); fn pick_start_mode(raw_braille: &str, use_only_grade1: bool) -> String { - // Need to decide what the start mode should be - // From http://www.brailleauthority.org/ueb/ueb_math_guidance/final_for_posting_ueb_math_guidance_may_2019_102419.pdf - // Unless a math expression can be correctly represented with only a grade 1 symbol indicator in the first three cells - // or before a single letter standing alone anywhere in the expression, - // begin the expression with a grade 1 word indicator (or a passage indicator if the expression includes spaces) - // Apparently "only a grade 1 symbol..." means at most one grade 1 symbol based on some examples (GTM 6.4, example 4) + // Decide grade 1 indicator placement per ICEB GTM 1.7 (2025). + // BANA's 2026 guidance adopts the same GTM §1.7 rules: + // https://www.brailleauthority.org/sites/default/files/2026-07/Guidance%20on%20Transcribing%20Math%20and%20Science%20in%20UEB%202026.pdf + // GTM 1.7.3 decides indicators per *symbols-sequence* (RUEB 2.1 -- text separated by spaces): + // (a) allow one grade 1 symbol per symbols-sequence, or a grade 1 word indicator if a sequence needs more than one; + // (b) use a grade 1 passage only if three or more symbols-sequences each need a grade 1 symbol or word indicator. + // Grade 1 symbol indicators forced by 'a-j' following a digit are not counted (GTM 1.7.3 note). // debug!("before determining mode: '{}'", raw_braille); - // a bit ugly because we need to store the string if we have cap passage mode - let raw_braille_string = if is_cap_passage_mode_good(raw_braille) {convert_to_cap_passage_mode(raw_braille)} else {String::default()}; - let raw_braille = if raw_braille_string.is_empty() {raw_braille} else {&raw_braille_string}; - if use_only_grade1 { - return remove_unneeded_mode_changes(raw_braille, UEB_Mode::Grade1, UEB_Duration::Passage); - } - let grade2 = remove_unneeded_mode_changes(raw_braille, UEB_Mode::Grade2, UEB_Duration::Symbol); - debug!("Symbol mode: '{}'", grade2); - - if is_grade2_string_ok(&grade2) { - return grade2; + // Capital passage (RUEB §8.5): like word mode's extra 'C' (CC…), passage uses CCC…Ce. + // Count G1 on the original (with capital markers) so standing-alone letters still force + // passage when needed (BANA Ex 5-15), but apply mode changes after stripping C/𝐶 so + // Grade 2 contractions are not blocked by cap_word_mode (chem "ch" → ⠡). + let had_unit_break = raw_braille.contains('𝐖'); + let raw_braille = raw_braille.replace('𝐖', "W"); + let use_cap_passage = is_cap_passage_mode_good(&raw_braille); + let stripped_caps = raw_braille.replace(['C', '𝐶'], ""); + let apply_to = if use_cap_passage { stripped_caps.as_str() } else { raw_braille.as_str() }; + let result = if use_only_grade1 { + remove_unneeded_mode_changes(apply_to, UEB_Mode::Grade1, UEB_Duration::Passage) } else { - // BANA says use g1 word mode if spaces are present, but that's not what their examples do - // A conversation with Ms. DeAndrea from BANA said that they mean use passage mode if ≥3 "segments" (≥2 blanks) - // The G1 Word mode might not be at the start (iceb.rs:omission_3_6_7) - let grade1_word = try_grade1_word_mode(raw_braille); - debug!("Word mode: '{}'", grade1_word); - if !grade1_word.is_empty() { - return grade1_word; - } else { - let grade1_passage = remove_unneeded_mode_changes(raw_braille, UEB_Mode::Grade1, UEB_Duration::Passage); - return "⠰⠰⠰".to_string() + &grade1_passage + "⠰⠄"; - } + gtm_1_7_mode(&raw_braille, apply_to, had_unit_break) + }; + if use_cap_passage { + return convert_to_cap_passage_mode(&result); } + return result; - /// Return true if at least five (= # of cap passage indicators) cap indicators and no lower case letters + /// Return true if capital passage mode should be used (RUEB §8.5 / BANA Ex 5-15). + /// Requires no lowercase letters, and either ≥5 capital indicators (chem / dense caps) + /// or ≥3 letter-bearing symbols-sequences that are fully capitalized. + /// Grade 1 / numeric / other non-letter markers are transparent (do not abort the scan). fn is_cap_passage_mode_good(braille: &str) -> bool { let mut n_caps = 0; + let mut n_letter_seqs = 0; + let mut seq_has_letter = false; let mut is_cap_mode = false; let mut cap_mode = UEB_Duration::Symbol; // real value set when is_cap_mode is set to true let mut chars = braille.chars(); @@ -1115,6 +1114,7 @@ fn ueb_cleanup(pref_manager: Ref, raw_braille: String) -> Str if !is_cap_mode { return false; } + seq_has_letter = true; chars.next(); // skip letter if cap_mode == UEB_Duration::Symbol { is_cap_mode = false; @@ -1130,76 +1130,33 @@ fn ueb_cleanup(pref_manager: Ref, raw_braille: String) -> Str } n_caps += 1; } else if ch == 'W' || ch == '𝐖' { - if is_cap_mode { - assert!(cap_mode == UEB_Duration::Word); + if seq_has_letter { + n_letter_seqs += 1; + seq_has_letter = false; } is_cap_mode = false; - } else if ch == '1' && is_cap_mode { - break; } + // else: '1', '𝟙', 'N', operators, typeforms, etc. — transparent } - return n_caps > 4; + if seq_has_letter { + n_letter_seqs += 1; + } + return n_caps >= 5 || n_letter_seqs >= 3; } + /// After G1 placement: strip any leftover capital markers and wrap with intermediate + /// `CCC`…`Ce` (same map as word mode: C→⠠, e→⠄ → ⠠⠠⠠…⠠⠄). Applied after + /// remove_unneeded_mode_changes so parsers never treat passage opener as CL letters. + /// If G1 passage is present (⠰⠰⠰…⠰⠄), capital passage goes inside (BANA Ex 5-15). fn convert_to_cap_passage_mode(braille: &str) -> String { - return "⠠⠠⠠".to_string() + &braille.replace(['C', '𝐶'], "") + "⠠⠄"; - } - - /// Return true if the BANA or ICEB guidelines say it is ok to start with grade 2 - fn is_grade2_string_ok(grade2_braille: &str) -> bool { - // BANA says use grade 2 if there is not more than one grade one symbol or single letter standing alone. - // The exact quote from their guidance: - // Unless a math expression can be correctly represented with only a grade 1 symbol indicator in the first three cells - // or before a single letter standing alone anywhere in the expression, - // begin the expression with a grade 1 word indicator - // Note: I modified this slightly to exclude the cap indicator in the count. That allows three more ICEB rule to pass and seems - // like it is a reasonable thing to do. - // Another modification is allow a single G1 indicator to occur after whitespace later on - // because ICEB examples show it and it seems better than going to passage mode if it is the only G1 indicator - - // Because of the 'L's which go away, we have to put a little more work into finding the first three chars - let chars = grade2_braille.chars().collect::>(); - let mut n_real_chars = 0; // actually number of chars - let mut found_g1 = false; - let mut i = 0; - while i < chars.len() { - let ch = chars[i]; - if ch == '1' && !is_forced_grade1(&chars, i) { - if found_g1 { - return false; - } - found_g1 = true; - } else if !"𝐶CLobc".contains(ch) { - if n_real_chars == 2 { - i += 1; - break; // this is the third real char - }; - n_real_chars += 1; - } - i += 1 - } - - // if we find *another* g1 that isn't forced and isn't standing alone, we are done - // I've added a 'follows whitespace' clause for test iceb.rs:omission_3_6_2 to the standing alone rule - // we only allow one standing alone example -- not sure if BANA guidance has this limit, but GTM 11_5_5_3 seems better with it - // Same for GTM 1_7_3_1 (passage mode is mentioned also) - let mut is_standing_alone_already_encountered = false; - let mut is_after_whitespace = false; - while i < chars.len() { - let ch = chars[i]; - if ch == 'W' { - is_after_whitespace = true; - } else if ch == '1' && !is_forced_grade1(&chars, i) { - if is_standing_alone_already_encountered || - ((found_g1 || !is_after_whitespace) && !is_single_letter_on_right(&chars, i)) { - return false; - } - found_g1 = true; - is_standing_alone_already_encountered = true; + let body = braille.replace(['C', '𝐶'], ""); + const G1_START: &str = "⠰⠰⠰"; + const G1_END: &str = "⠰⠄"; + if let Some(rest) = body.strip_prefix(G1_START) + && let Some(mid) = rest.strip_suffix(G1_END) { + return format!("{G1_START}CCC{mid}Ce{G1_END}"); } - i += 1; - } - return true; + return format!("CCC{body}Ce"); } /// Return true if the sequence of chars forces a '1' at the `i`th position @@ -1221,56 +1178,139 @@ fn ueb_cleanup(pref_manager: Ref, raw_braille: String) -> Str return false; } - fn is_single_letter_on_right(chars: &[char], i: usize) -> bool { - fn is_skip_char(ch: char) -> bool { - matches!(ch, 'B' | 'I' | '𝔹' | 'S' | 'T' | 'D' | 'C' | '𝐶' | 's' | 'w') - } + /// Count the number of non-forced grade 1 indicators needed for a single symbols-sequence. + /// (Forced indicators -- 'a-j' following a digit -- are excluded per the GTM 1.7.3 note.) + fn grade1_count(raw_word: &str) -> usize { + let grade2 = remove_unneeded_mode_changes(raw_word, UEB_Mode::Grade2, UEB_Duration::Symbol); + let chars = grade2.chars().collect::>(); + return chars.iter().enumerate() + .filter(|&(i, &ch)| ch == '1' && !is_forced_grade1(&chars, i)) + .count(); + } - // find the first char (if any) - let mut count = 0; // how many letters - let mut i = i+1; + /// True if this symbols-sequence contains a literary English word (4+ letters). + /// GTM 1.7.5: short math names (sin, cos, lim, min, …) and single-letter variables + /// are not treated as words. + fn sequence_contains_word(raw_word: &str) -> bool { + let chars: Vec = raw_word.chars().collect(); + let mut run = 0usize; + let mut i = 0usize; while i < chars.len() { let ch = chars[i]; - if !is_skip_char(ch) { - if ch == 'L' { - if count == 1 { - return false; // found a second letter in the sequence - } - count += 1; - } else { - return count==1; + if ch == 'L' { + run += 1; + i += 1; + if i < chars.len() { + i += 1; // skip the braille cell after 'L' + } + if run >= 4 { + return true; } - i += 2; // eat 'L' and actual letter + } else if ch == 'A' { + run += 1; + i += 1; + if run >= 4 { + return true; + } + } else if is_math_alphabet_typeform(ch) { + // Double-struck/script/fraktur/sans-serif letters are not part of an English word. + let end = math_typeform_item_end(&chars, i); + i = if end > i { end } else { i + 1 }; + run = 0; + } else if matches!(ch, 'C' | '𝐶' | 'G' | 'V' | 'B' | 'I' | 's' | 'w' | 'e') { + i += 1; // letter prefixes / literary typeform — do not break the run } else { + run = 0; i += 1; } } - return true; + false + } + + /// G2 form of a general-fraction sequence when two symbol indicators keep words + /// contracted (GTM 1.7.5(a)). `None` if more than two G1 symbols would remain. + fn g2_two_symbol_form(raw_word: &str) -> Option { + // Only the GTM 1.7.5(a) case: a general fraction whose numerator/denominator + // contain literary words. Superscripts, radicals, etc. keep 1.7.3 word indicators. + if !(raw_word.contains('⠷') && raw_word.contains("⠨⠌")) { + return None; + } + let g2 = remove_unneeded_mode_changes(raw_word, UEB_Mode::Grade2, UEB_Duration::Symbol); + let chars: Vec = g2.chars().collect(); + let n_g1 = chars.iter().enumerate() + .filter(|&(i, &ch)| ch == '1' && !is_forced_grade1(&chars, i)) + .count(); + if n_g1 <= 2 { + Some(g2) + } else { + None + } } - fn try_grade1_word_mode(raw_braille: &str) -> String { - // this isn't quite right, but pretty close -- try splitting at 'W' (words) - // only one of the parts can be in word mode and none of the others can have '1' unless forced - let mut g1_words = Vec::default(); - let mut found_word_mode = false; - for raw_word in raw_braille.split('W') { - let word = remove_unneeded_mode_changes(raw_word, UEB_Mode::Grade2, UEB_Duration::Symbol); - // debug!("try_grade1_word_mode: word='{}'", word); - let word_chars = word.chars().collect::>(); - let needs_word_mode = word_chars.iter().enumerate() - .any(|(i, &ch) | ch == '1' && !is_forced_grade1(&word_chars, i)); - if needs_word_mode { - if found_word_mode { - return "".to_string(); + /// ICEB GTM 1.7 grade 1 indicator placement (per symbols-sequence). + /// GTM 1.7.3: one grade 1 symbol per sequence, or a word indicator if more than one. + /// GTM 1.7.5(a): for a sequence containing word(s), allow two grade 1 symbol + /// indicators when that keeps the words in their usual contracted form + /// (e.g. ⠷…⠾ around work/distance). More than two still uses a word indicator. + /// `count_src` decides passage vs word vs symbol; changes are applied to `apply_to` + /// (may differ when capital markers were stripped for capital-passage mode). + fn gtm_1_7_mode(count_src: &str, apply_to: &str, had_unit_break: bool) -> String { + let count_words: Vec<&str> = count_src.split('W').collect(); + let apply_words: Vec<&str> = apply_to.split('W').collect(); + assert_eq!(count_words.len(), apply_words.len(), + "gtm_1_7_mode: count_src and apply_to must have the same number of symbols-sequences"); + + // Count how many symbols-sequences (whitespace-separated) need a non-forced grade 1 indicator. + let n_seq_needing = count_words.iter().filter(|raw_word| grade1_count(raw_word) >= 1).count(); + + // GTM 1.7.3(b) / 1.7.5(c): use a grade 1 passage if three or more sequences + // each need grade 1. 1.7.5(c) starts the passage at the first such sequence so + // preceding words (e.g. "speed") stay contracted. That delayed start is used when + // spaced units (𝐖) joined the expression; otherwise a passage wraps the whole + // expression (chem, roots, labelled equations). + if n_seq_needing >= 3 { + let first = count_words.iter().position(|w| grade1_count(w) >= 1) + .expect("n_seq_needing >= 3 implies a sequence that needs grade 1"); + let delay_start = had_unit_break && first > 0; + if delay_start { + let prefix: Vec = apply_words[..first].iter() + .map(|w| remove_unneeded_mode_changes(w, UEB_Mode::Grade2, UEB_Duration::Symbol)) + .collect(); + let rest = apply_words[first..].join("W"); + let passage = remove_unneeded_mode_changes(&rest, UEB_Mode::Grade1, UEB_Duration::Passage); + let mut out = prefix.join("W"); + if !out.is_empty() { + out.push('W'); } - found_word_mode = true; - g1_words.push("⠰⠰".to_string() + &remove_unneeded_mode_changes(raw_word, UEB_Mode::Grade1, UEB_Duration::Word) - ); - } else { - g1_words.push(word); + out.push_str("⠰⠰⠰"); + out.push_str(&passage); + out.push_str("⠰⠄"); + return out; } + let grade1_passage = remove_unneeded_mode_changes(apply_to, UEB_Mode::Grade1, UEB_Duration::Passage); + return "⠰⠰⠰".to_string() + &grade1_passage + "⠰⠄"; } - return if found_word_mode {g1_words.join("W")} else {"".to_string()}; + + // GTM 1.7.3(a): per sequence, allow one grade 1 symbol indicator, or a grade 1 word + // indicator if the sequence needs more than one. + let words = count_words.iter().zip(apply_words.iter()) + .map(|(&count_word, &apply_word)| { + let n_g1 = grade1_count(count_word); + if n_g1 >= 2 { + // GTM 1.7.5(a): two symbol indicators if that preserves contractions + if sequence_contains_word(count_word) + && let Some(g2) = g2_two_symbol_form(apply_word) { + g2 + } else { + "⠰⠰".to_string() + &remove_unneeded_mode_changes(apply_word, UEB_Mode::Grade1, UEB_Duration::Word) + } + } else { + // 0 or 1 grade 1 indicators: the grade 2 form leaves a single symbol indicator inline + remove_unneeded_mode_changes(apply_word, UEB_Mode::Grade2, UEB_Duration::Symbol) + } + }) + .collect::>(); + return words.join("W"); } } } @@ -1510,6 +1550,93 @@ fn is_left_intervening_char(ch: char) -> bool { matches!(ch, 'B' | 'I' | '𝔹' | 'S' | 'T' | 'D' | 'C' | '𝐶' | 's' | 'w') } +/// Double-struck / script / fraktur / sans-serif: math alphabets, not literary emphasis. +fn is_math_alphabet_typeform(ch: char) -> bool { + matches!(ch, '𝔹' | 'T' | 'D' | 'S') +} + +/// Advance past one letter with optional capital, Greek, and accent prefixes (`C? G|V? A…? L cell`). +fn skip_one_prefixed_letter(chars: &[char], mut i: usize) -> usize { + if i >= chars.len() { + return i; + } + if matches!(chars[i], 'C' | '𝐶') { + i += 1; + } + if i < chars.len() && matches!(chars[i], 'G' | 'V') { + i += 1; + } + if i < chars.len() && chars[i] == 'A' { + match index_after_accent_to_l(chars, i + 1) { + Some(i_l) => i = i_l, + None => return i, + } + } + if i < chars.len() && chars[i] == 'L' { + i += 1; + if i < chars.len() { + i += 1; // braille cell + } + } + i +} + +/// End index of a math-alphabet typeform item starting at `i` (`𝔹`/`T`/`D`/`S`). +/// `s` covers one letter; `w` covers the typeform word until its terminator. +fn math_typeform_item_end(chars: &[char], i: usize) -> usize { + if i >= chars.len() || !is_math_alphabet_typeform(chars[i]) { + return i; + } + let typeform = chars[i]; + let mut j = i + 1; + let word_mode = j < chars.len() && chars[j] == 'w'; + if j < chars.len() && matches!(chars[j], 's' | 'w') { + j += 1; + } + if word_mode { + loop { + let next = skip_one_prefixed_letter(chars, j); + if next == j { + break; + } + j = next; + } + if j + 1 < chars.len() && chars[j] == typeform && chars[j + 1] == 'e' { + j += 2; + } else if j < chars.len() && chars[j] == 'e' { + j += 1; + } + return j; + } + return skip_one_prefixed_letter(chars, j); +} + +/// RUEB 10.6.2: lower groupsigns "be", "con", "dis" only at the beginning of a word. +/// Beginning of a word = letters-sequence after space, hyphen or dash, optionally with +/// intervening punctuation/indicators from RUEB 2.6.2 (opening brackets/quotes, typeform, +/// capitals). Math operators and fraction indicators are not word boundaries. +/// `word_start` is the index of the first char of a grade-2 letter run ('L', 'C', or 'A'). +fn allows_lower_word_sign_at(chars: &[char], word_start: usize) -> bool { + if word_start >= chars.len() { + return false; + } + if word_start == 0 { + return true; + } + let mut j = word_start; + while j > 0 { + j -= 1; + let ch = chars[j]; + // 2.6.2 intervening symbols (opening punctuation flags, typeform, capitals, etc.) + if is_left_intervening_char(ch) || matches!(ch, 'e' | 'c' | 's' | 'w' | 'o' | 'b' | 'G' | 'V') { + continue; + } + // Must be space, hyphen or dash (RUEB 10.6.2 Note) + return "W𝐖-—―".contains(ch); + } + true +} + /// Return value for use_g1_word_mode() #[derive(Debug, PartialEq)] enum Grade1WordIndicator { @@ -1677,6 +1804,34 @@ fn remove_unneeded_mode_changes(raw_braille: &str, start_mode: UEB_Mode, start_d } // debug!("Grade 2: ch={}, duration: {:?}", ch, duration); match ch { + '𝔹' | 'T' | 'D' | 'S' => { + // Math-alphabet typeform: this letter is not part of an English word + // (GTM 11.6). Close any current contraction run, emit the typeform + // item on its own, then let following letters start a new word. + if let Some(start) = start_g2_letter { + if !cap_word_mode { + result = handle_contractions(&chars, start, i, result); + } + cap_word_mode = false; + start_g2_letter = None; + } + let unit_end = math_typeform_item_end(&chars, i); + // Typeform prefix (`𝔹s`) must precede the grade 1 indicator: ⠈⠆⠰⠠⠗, not ⠰⠈⠆⠠⠗. + let mut letter_start = i + 1; + if letter_start < unit_end && matches!(chars[letter_start], 's' | 'w') { + letter_start += 1; + } + result.extend(chars[i..letter_start].iter().copied()); + if let Some(letter_idx) = chars[i..unit_end].iter().position(|&c| c == 'L') { + let (is_alone, _, n_letters) = stands_alone(&chars, i + letter_idx); + if is_alone && n_letters == 1 { + result.push('1'); + mode = UEB_Mode::Grade1; + } + } + result.extend(chars[letter_start..unit_end].iter().copied()); + i = unit_end; + }, 'L' => { if start_g2_letter.is_none() { start_g2_letter = Some(i); @@ -1758,6 +1913,16 @@ fn remove_unneeded_mode_changes(raw_braille: &str, start_mode: UEB_Mode, start_d } }, '1' => { + // Contract the preceding letter run before consuming '1'. The '1' arm used + // to increment first, so handle_contractions saw "work1" and the whole-word + // patterns `^work$` / `^time$` failed (GTM 1.7.9 work/distance, 1.7.5(a) time). + if let Some(start) = start_g2_letter { + if !cap_word_mode { + result = handle_contractions(&chars, start, i, result); + } + cap_word_mode = false; + start_g2_letter = None; + } result.push(ch); i += 1; mode = UEB_Mode::Grade1; @@ -1778,7 +1943,7 @@ fn remove_unneeded_mode_changes(raw_braille: &str, start_mode: UEB_Mode, start_d _ => { if let Some(start) = start_g2_letter { if !cap_word_mode { - result = handle_contractions(&chars[start..i], result); + result = handle_contractions(&chars, start, i, result); } cap_word_mode = false; start_g2_letter = None; // not start of char sequence @@ -1794,7 +1959,7 @@ fn remove_unneeded_mode_changes(raw_braille: &str, start_mode: UEB_Mode, start_d } if mode != UEB_Mode::Grade2 && !cap_word_mode && let Some(start) = start_g2_letter { - result = handle_contractions(&chars[start..i], result); + result = handle_contractions(&chars, start, i, result); start_g2_letter = None; // not start of char sequence } }, @@ -1814,7 +1979,7 @@ fn remove_unneeded_mode_changes(raw_braille: &str, start_mode: UEB_Mode, start_d } if mode == UEB_Mode::Grade2 && let Some(start) = start_g2_letter { - result = handle_contractions(&chars[start..i], result); + result = handle_contractions(&chars, start, i, result); } return result; @@ -1946,10 +2111,12 @@ fn stands_alone(chars: &[char], i: usize) -> (bool, &[char], usize) { /// Return a modified result if chars can be contracted. /// Otherwise, the original string is returned -fn handle_contractions(chars: &[char], mut result: String) -> String { +fn handle_contractions(full_chars: &[char], start: usize, end: usize, mut result: String) -> String { struct Replacement { pattern: String, - replacement: &'static str + replacement: &'static str, + skip_if_word_in: Option<&'static phf::Set<&'static str>>, + word_start_only: bool, } const ASCII_TO_UNICODE: &[char] = &[ @@ -1968,47 +2135,95 @@ fn handle_contractions(chars: &[char], mut result: String) -> String { return unicode; } + // Whole words where the lower groupsign 'cc' must not apply (10.11.1 compound/function names). + static CC_EXCEPTION_WORDS: phf::Set<&str> = phf_set! { + "L⠁L⠗L⠉L⠉L⠕L⠎", // arccos + "L⠁L⠗L⠉L⠉L⠕L⠎L⠊L⠝L⠑", // arccosine + "L⠁L⠗L⠉L⠉L⠕L⠎L⠓", // arccosh + "L⠁L⠗L⠉L⠉L⠕L⠎L⠑L⠉", // arccosec + "L⠁L⠗L⠉L⠉L⠕L⠎L⠑L⠉L⠁L⠝L⠞", // arccosecant + "L⠁L⠗L⠉L⠉L⠕L⠞", // arccot + "L⠁L⠗L⠉L⠉L⠕L⠞L⠁L⠝L⠛L⠑L⠝L⠞", // arccotangent + "L⠁L⠗L⠉L⠉L⠎L⠉", // arccsc + }; + + // Words where the 'ea' lower groupsign must not apply (re- prefix morpheme boundary). + static EA_EXCEPTION_WORDS: phf::Set<&str> = phf_set! { + "L⠗L⠑L⠁L⠉L⠞L⠁L⠝L⠉L⠑", // reactance + "L⠗L⠑L⠁L⠉L⠞L⠊L⠕L⠝", // reaction + "L⠗L⠑L⠁L⠉L⠞L⠊L⠧L⠊L⠞L⠽", // reactivity + }; + + // Words where "be" is not the first syllable (RUEB 10.6.1 "But:" cases and similar). + static BE_EXCEPTION_WORDS: phf::Set<&str> = phf_set! { + "L⠃L⠑L⠁L⠗L⠊L⠝L⠛", // bearing + "L⠃L⠑L⠝L⠉L⠓L⠍L⠁L⠗L⠅", // benchmark + }; + // It would be much better from an extensibility point of view to read the table in from a file static CONTRACTIONS: LazyLock> = LazyLock::new(|| { vec![ + // 10.9: initial-letter (dot-5) wordsigns -- whole word only + Replacement{ pattern: format!("^{}$", to_unicode_braille("time")), replacement: "⠐⠞", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: format!("^{}$", to_unicode_braille("work")), replacement: "⠐⠺", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: format!("^{}$", to_unicode_braille("leverage")), replacement: "⠇⠐⠑⠁⠛⠑", skip_if_word_in: None, word_start_only: false }, + + // 10.7.1: dot-5 initial-letter contractions (as wordsigns / groupsigns) + Replacement{ pattern: format!("^{}", to_unicode_braille("through")), replacement: "⠐⠹", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: format!("(?PL.){}(?PL.)", to_unicode_braille("part")), replacement: "${s}⠐⠏${e}", skip_if_word_in: None, word_start_only: false }, + // 10.3: Strong contractions - Replacement{ pattern: to_unicode_braille("and"), replacement: "L⠯"}, - Replacement{ pattern: to_unicode_braille("for"), replacement: "L⠿"}, - Replacement{ pattern: to_unicode_braille("of"), replacement: "L⠷"}, - Replacement{ pattern: to_unicode_braille("the"), replacement: "L⠮"}, - Replacement{ pattern: to_unicode_braille("with"), replacement: "L⠾"}, + Replacement{ pattern: to_unicode_braille("and"), replacement: "L⠯", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("for"), replacement: "L⠿", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("of"), replacement: "L⠷", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("the"), replacement: "L⠮", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("with"), replacement: "L⠾", skip_if_word_in: None, word_start_only: false }, + + // 10.6.1-10.6.4 / 10.10.4: lower groupsigns be/con/dis -- only at word start, + // and preferred over other groupsigns (e.g. "distance" is ⠲⠞⠨⠑, not di+st+ance). + Replacement{ pattern: format!("^{}", to_unicode_braille("be")), replacement: "⠆", skip_if_word_in: Some(&BE_EXCEPTION_WORDS), word_start_only: true }, + Replacement{ pattern: format!("^{}", to_unicode_braille("con")), replacement: "⠒", skip_if_word_in: None, word_start_only: true }, + Replacement{ pattern: format!("^{}", to_unicode_braille("dis")), replacement: "⠲", skip_if_word_in: None, word_start_only: true }, - // 10.8: final-letter group signs (this need to precede 'en' and any other shorter contraction) - Replacement{ pattern: "(?PL.)L⠍L⠑L⠝L⠞".to_string(), replacement: "${s}L⠰L⠞" }, // ment - Replacement{ pattern: "(?PL.)L⠞L⠊L⠕L⠝".to_string(), replacement: "${s}L⠰L⠝" } ,// tion + // 10.8: final-letter group signs (these need to precede 'en' and any other shorter contraction) + Replacement{ pattern: "(?PL.)L⠍L⠑L⠝L⠞".to_string(), replacement: "${s}L⠰L⠞", skip_if_word_in: None, word_start_only: false }, // ment + Replacement{ pattern: "(?PL.)L⠞L⠊L⠕L⠝".to_string(), replacement: "${s}L⠰L⠝", skip_if_word_in: None, word_start_only: false }, // tion + Replacement{ pattern: "(?PL.)L⠊L⠞L⠽".to_string(), replacement: "${s}L⠰L⠽", skip_if_word_in: None, word_start_only: false }, // ity + Replacement{ pattern: "(?PL.)L⠁L⠝L⠉L⠑".to_string(), replacement: "${s}L⠨L⠑", skip_if_word_in: None, word_start_only: false }, // ance + Replacement{ pattern: "(?PL.)L⠎L⠊L⠕L⠝".to_string(), replacement: "${s}L⠨L⠝", skip_if_word_in: None, word_start_only: false }, // sion + Replacement{ pattern: "(?PL.)L⠑L⠝L⠉L⠑".to_string(), replacement: "${s}L⠰L⠑", skip_if_word_in: None, word_start_only: false }, // ence + Replacement{ pattern: "(?PL.)L⠝L⠑L⠎L⠎".to_string(), replacement: "${s}L⠰L⠎", skip_if_word_in: None, word_start_only: false }, // ness + Replacement{ pattern: "(?PL.)L⠕L⠥L⠝L⠙".to_string(), replacement: "${s}L⠨L⠙", skip_if_word_in: None, word_start_only: false }, // ound + Replacement{ pattern: "(?PL.)L⠕L⠥L⠝L⠞".to_string(), replacement: "${s}L⠨L⠞", skip_if_word_in: None, word_start_only: false }, // ount + Replacement{ pattern: "(?PL.)L⠇L⠑L⠎L⠎".to_string(), replacement: "${s}L⠨L⠎", skip_if_word_in: None, word_start_only: false }, // less + Replacement{ pattern: "(?PL.)L⠕L⠝L⠛".to_string(), replacement: "${s}L⠰L⠛", skip_if_word_in: None, word_start_only: false }, // ong + Replacement{ pattern: "(?PL.)L⠋L⠥L⠇".to_string(), replacement: "${s}L⠰L⠇", skip_if_word_in: None, word_start_only: false }, // ful // 10.4: Strong group signs - Replacement{ pattern: to_unicode_braille("ch"), replacement: "L⠡"}, - Replacement{ pattern: to_unicode_braille("gh"), replacement: "L⠣"}, - Replacement{ pattern: to_unicode_braille("sh"), replacement: "L⠩"}, - Replacement{ pattern: to_unicode_braille("th"), replacement: "L⠹"}, - Replacement{ pattern: to_unicode_braille("wh"), replacement: "L⠱"}, - Replacement{ pattern: to_unicode_braille("ed"), replacement: "L⠫"}, - Replacement{ pattern: to_unicode_braille("er"), replacement: "L⠻"}, - Replacement{ pattern: to_unicode_braille("ou"), replacement: "L⠳"}, - Replacement{ pattern: to_unicode_braille("ow"), replacement: "L⠪"}, - Replacement{ pattern: to_unicode_braille("st"), replacement: "L⠌"}, - Replacement{ pattern: "(?PL.)L⠊L⠝L⠛".to_string(), replacement: "${s}L⠬" }, // 'ing', not at start - Replacement{ pattern: to_unicode_braille("ar"), replacement: "L⠜"}, + Replacement{ pattern: to_unicode_braille("ch"), replacement: "L⠡", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("gh"), replacement: "L⠣", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("sh"), replacement: "L⠩", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("th"), replacement: "L⠹", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("wh"), replacement: "L⠱", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("ed"), replacement: "L⠫", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("er"), replacement: "L⠻", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("ou"), replacement: "L⠳", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("ow"), replacement: "L⠪", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("st"), replacement: "L⠌", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: "(?PL.)L⠊L⠝L⠛".to_string(), replacement: "${s}L⠬", skip_if_word_in: None, word_start_only: false }, // 'ing', not at start + Replacement{ pattern: to_unicode_braille("ar"), replacement: "L⠜", skip_if_word_in: None, word_start_only: false }, // 10.6.5: Lower group signs preceded and followed by letters // FIX: don't match if after/before a cap letter -- can't use negative pattern (?!...) in regex package - // Note: removed cc because "arccos" shouldn't be contracted (10.11.1), but there is no way to know about compound words - // Add it back after implementing a lookup dictionary of exceptions - Replacement{ pattern: "(?PL.)L⠑L⠁(?PL.)".to_string(), replacement: "${s}L⠂${e}" }, // ea - Replacement{ pattern: "(?PL.)L⠃L⠃(?PL.)".to_string(), replacement: "${s}L⠆${e}" }, // bb - // Replacement{ pattern: "(?PL.)L⠉L⠉(?PL.)".to_string(), replacement: "${s}L⠒${e}" }, // cc - Replacement{ pattern: "(?PL.)L⠋L⠋(?PL.)".to_string(), replacement: "${s}L⠖${e}" }, // ff - Replacement{ pattern: "(?PL.)L⠛L⠛(?PL.)".to_string(), replacement: "${s}L⠶${e}" }, // gg - - // 10.6.8: Lower group signs ("in" also 10.5.4 lower word signs) + Replacement{ pattern: "(?PL.)L⠑L⠁(?PL.)".to_string(), replacement: "${s}L⠂${e}", skip_if_word_in: Some(&EA_EXCEPTION_WORDS), word_start_only: false }, // ea + Replacement{ pattern: "(?PL.)L⠃L⠃(?PL.)".to_string(), replacement: "${s}L⠆${e}", skip_if_word_in: None, word_start_only: false }, // bb + Replacement{ pattern: "(?PL.)L⠉L⠉(?PL.)".to_string(), replacement: "${s}L⠒${e}", skip_if_word_in: Some(&CC_EXCEPTION_WORDS), word_start_only: false }, // cc + Replacement{ pattern: "(?PL.)L⠋L⠋(?PL.)".to_string(), replacement: "${s}L⠖${e}", skip_if_word_in: None, word_start_only: false }, // ff + Replacement{ pattern: "(?PL.)L⠛L⠛(?PL.)".to_string(), replacement: "${s}L⠶${e}", skip_if_word_in: None, word_start_only: false }, // gg + + // 10.6.8: lower group signs; also 10.6.2 word signs when at word start ("sin", etc.) // FIX: these need restrictions about only applying when upper dots are present - Replacement{ pattern: to_unicode_braille("en"), replacement: "⠢"}, - Replacement{ pattern: to_unicode_braille("in"), replacement: "⠔"}, + Replacement{ pattern: to_unicode_braille("en"), replacement: "⠢", skip_if_word_in: None, word_start_only: false }, + Replacement{ pattern: to_unicode_braille("in"), replacement: "⠔", skip_if_word_in: None, word_start_only: false }, ] }); @@ -2016,11 +2231,28 @@ fn handle_contractions(chars: &[char], mut result: String) -> String { static CONTRACTION_PATTERNS: LazyLock = LazyLock::new(|| init_patterns(&CONTRACTIONS)); static CONTRACTION_REGEX: LazyLock> = LazyLock::new(|| init_regex(&CONTRACTIONS)); + let word_start_ok = allows_lower_word_sign_at(full_chars, start); + let chars = &full_chars[start..end]; + // A non-English letter glued to an English word (GTM 1.7.9 Δtime, or a + // double-struck/script/fraktur letter) is not part of the English + // letters-sequence; contract the following word on its own (time → ⠐⠞). + let skip = leading_non_word_letter_len(chars); + if skip > 0 && skip < chars.len() { + return handle_contractions(full_chars, start + skip, end, result); + } let mut chars_as_str = chars.iter().collect::(); + let original_chars_as_str = chars_as_str.clone(); // debug!(" handle_contractions: examine '{}'", &chars_as_str); let matches = CONTRACTION_PATTERNS.matches(&chars_as_str); for i in matches.iter() { let element = &CONTRACTIONS[i]; + if let Some(exceptions) = element.skip_if_word_in + && exceptions.contains(&original_chars_as_str) { + continue; + } + if element.word_start_only && !word_start_ok { + continue; + } // debug!(" replacing '{}' with '{}' in '{}'", element.pattern, element.replacement, &chars_as_str); result.truncate(result.len() - chars_as_str.len()); chars_as_str = CONTRACTION_REGEX[i].replace_all(&chars_as_str, element.replacement).to_string(); @@ -2031,6 +2263,31 @@ fn handle_contractions(chars: &[char], mut result: String) -> String { + fn leading_non_word_letter_len(chars: &[char]) -> usize { + if chars.is_empty() { + return 0; + } + if is_math_alphabet_typeform(chars[0]) { + let end = math_typeform_item_end(chars, 0); + return if end < chars.len() { end } else { 0 }; + } + let mut i = 0; + if i < chars.len() && matches!(chars[i], 'C' | '𝐶') { + i += 1; + } + if i < chars.len() && matches!(chars[i], 'G' | 'V') { + i += 1; + if i < chars.len() && chars[i] == 'L' { + i += 1; + if i < chars.len() { + i += 1; // braille cell + } + return i; + } + } + 0 + } + fn init_patterns(contractions: &[Replacement]) -> RegexSet { let mut vec: Vec<&str> = Vec::with_capacity(contractions.len()); for contraction in contractions { @@ -3808,6 +4065,26 @@ mod tests { }); } + #[test] + fn ueb_lower_word_sign_contractions() -> Result<()> { + return braille_test(|| { + init_braille_mathml("distance")?; + set_preference("BrailleCode", "UEB")?; + let distance = get_braille("")?; + // RUEB 10.6.1 / 10.10.4: distance at word start uses dis lower groupsign + assert_eq!(distance, "⠲⠞⠨⠑", "dis lower groupsign at word start: got '{distance}'"); + // After fraction open (not a RUEB 10.6.2 word start), dis must not apply; st+ance may. + init_braille_mathml("distancetime")?; + let frac = get_braille("")?; + assert!(!frac.contains("⠲"), "dis wrongly used after fraction open: got '{frac}'"); + assert!(frac.contains("⠙⠊⠌⠨⠑") || frac.contains("⠙⠊⠎⠞⠁⠝⠉⠑"), + "expected st+ance or uncontracted distance after fraction open: got '{frac}'"); + init_braille_mathml("include")?; + assert!(get_braille("")?.starts_with("⠔"), "in lower groupsign at word start"); + return Ok(()); + }); + } + #[test] // This test probably should be repeated for each braille code and be taken out of here fn find_mathml_from_braille() -> Result<()> { @@ -3868,7 +4145,7 @@ mod tests { set_preference("BrailleCode", "UEB")?; let _braille = get_braille("")?; - let answers= &[0, 0, 0, 2, 3, 3, 3, 3, 4, 7, 7, 8, 9, 9, 10, 13, 12, 14, 14, 15, 15, 17, 17, 19, 19, 21, 10, 4, 4, 23, 23, 25, 25, 4, 0, 0]; + let answers= &[2, 2, 3, 3, 3, 3, 4, 4, 4, 7, 7, 8, 9, 9, 10, 13, 12, 14, 14, 15, 15, 17, 17, 19, 19, 21, 10, 4, 4, 23, 23, 25, 25, 4]; let answers = answers.map(|num| format!("id-{}", num)); debug!("\n\n*** Testing UEB ***"); for (i, answer) in answers.iter().enumerate() { @@ -3927,4 +4204,367 @@ mod tests { "oL⠈N⠹⠜L⠁cL⠈N⠩⠜L⠃" ); } + + /// Letter-to-Unicode-braille mapping (matches handle_contractions). + const TEST_ASCII_TO_UNICODE: &[char] = &[ + '⠀', '⠮', '⠐', '⠼', '⠫', '⠩', '⠯', '⠄', '⠷', '⠾', '⠡', '⠬', '⠠', '⠤', '⠨', '⠌', + '⠴', '⠂', '⠆', '⠒', '⠲', '⠢', '⠖', '⠶', '⠦', '⠔', '⠱', '⠰', '⠣', '⠿', '⠜', '⠹', + '⠈', '⠁', '⠃', '⠉', '⠙', '⠑', '⠋', '⠛', '⠓', '⠊', '⠚', '⠅', '⠇', '⠍', '⠝', '⠕', + '⠏', '⠟', '⠗', '⠎', '⠞', '⠥', '⠧', '⠺', '⠭', '⠽', '⠵', '⠪', '⠳', '⠻', '⠘', '⠸', + ]; + + fn ascii_to_uncontracted_braille(word: &str) -> String { + word.bytes() + .map(|b| TEST_ASCII_TO_UNICODE[(b.to_ascii_uppercase() - 32) as usize]) + .collect() + } + + /// Run handle_contractions on a lowercase ASCII word (L-cell input) and return Unicode braille. + fn contract_word(word: &str) -> String { + let l_chars: Vec = word.bytes() + .flat_map(|b| { + let ch = TEST_ASCII_TO_UNICODE[(b.to_ascii_uppercase() - 32) as usize]; + ['L', ch] + }) + .collect(); + let input: String = l_chars.iter().collect(); + let output = handle_contractions(&l_chars, 0, l_chars.len(), input); + output.chars().filter(|&c| c != 'L').collect() + } + + /// Grade-2 contraction coverage for technical vocabulary (311 words). + /// Cross-check goldens at https://brailletranslators.com/ (Grade 2 / UEB). + /// Should contract (225): force, work, time, speed, distance, area, height, weight, power, energy, density, gravity, acceleration, length, width, depth, cost, profit, interest, revenue, clearance, conductance, capacitance, resistance, impedance, reactance, variance, tolerance, luminosity, reliability, velocity, viscosity, precision, tension, efficiency, frequency, valence, stiffness, thickness, displacement, concentration, consumption, duration, elevation, fraction, friction, inflation, iteration, population, position, potential, production, resolution, rotation, charge, chord, pitch, growth, head, heat, reach, shear, margin, salary, offset, income, input, inventory, current, error, leverage, temperature, turnover, accuracy, breadth, strength, spread, discount, count, version, inductance, intensity, interval, perimeter, probability, utility, validity, parity, permeability, proportion, uncertainty, circumference, capacity, deceleration, dilution, enthalpy, entropy, equity, expense, gradient, headroom, inertia, latency, longitude, permittivity, period, principal, sensitivity, stress, throughput, thrust, wavelength, absorption, activation, activity, admittance, affinity, albedo, aperture, attenuation, average, bandwidth, baseline, bearing, benchmark, boiling, boundary, cohesion, compliance, compression, conductivity, coverage, damping, deflection, departure, depreciation, deviation, diffusion, dispersion, dissipation, distortion, divergence, downforce, elasticity, emissivity, emission, erosion, evaporation, expansion, expectation, extent, feedback, fidelity, filament, hardness, humidity, hysteresis, illuminance, incidence, index, inflow, influx, inhibition, injection, irradiance, kinetic, leakage, luminance, maturity, mean, median, melting, minimum, mobility, molarity, moisture, opacity, oscillation, outflow, overlap, parallax, parameter, peak, percentile, percentage, polarization, propagation, purity, quality, quantity, quenching, quotient, radiance, radiation, reaction, reactivity, redshift, reflectance, reflection, refraction, reluctance, remanence, resistivity, resonance, restitution, retardation, rigidity, salinity, saturation, scalar, scattering, separation, shrinkage, similarity, battery, diameter, epoch, gain, jitter, joule, momentum, shift, step, stride, zenith + /// Should not contract (86): mass, volume, base, rate, drag, drift, gap, lag, loss, pace, radius, range, ratio, risk, run, scale, size, slope, tax, torque, capital, impulse, latitude, magnitude, premium, pressure, response, return, trajectory, voltage, aspect, backlog, curvature, dosage, dose, equilibrium, excess, exposure, fatigue, hydraulic, lifetime, likelihood, maximum, metric, orbit, osmosis, pulse, quanta, quantum, regret, residual, ripple, root, rotor, sag, secant, signal, altitude, amplitude, angle, bias, bitrate, budget, bulk, decay, deficit, degree, delay, duty, lift, limit, load, modulus, payload, phase, price, sample, skew, slack, span, supply, total, value, vector, wage, yield + /// Note: arccos (cc exception, partial ar groupsign) is covered in tests/braille/UEB/other.rs contractions_1. + #[test] + fn ueb_technical_word_contractions() { + const SHOULD_CONTRACT: &[(&str, &str)] = &[ + ("force", "⠿⠉⠑"), + ("work", "⠐⠺"), + ("time", "⠐⠞"), + ("speed", "⠎⠏⠑⠫"), + ("distance", "⠲⠞⠨⠑"), + ("area", "⠜⠑⠁"), + ("height", "⠓⠑⠊⠣⠞"), + ("weight", "⠺⠑⠊⠣⠞"), + ("power", "⠏⠪⠻"), + ("energy", "⠢⠻⠛⠽"), + ("density", "⠙⠢⠎⠰⠽"), + ("gravity", "⠛⠗⠁⠧⠰⠽"), + ("acceleration", "⠁⠒⠑⠇⠻⠁⠰⠝"), + ("length", "⠇⠢⠛⠹"), + ("width", "⠺⠊⠙⠹"), + ("depth", "⠙⠑⠏⠹"), + ("cost", "⠉⠕⠌"), + ("profit", "⠏⠗⠷⠊⠞"), + ("interest", "⠔⠞⠻⠑⠌"), + ("revenue", "⠗⠑⠧⠢⠥⠑"), + ("clearance", "⠉⠇⠑⠜⠨⠑"), + ("conductance", "⠒⠙⠥⠉⠞⠨⠑"), + ("capacitance", "⠉⠁⠏⠁⠉⠊⠞⠨⠑"), + ("resistance", "⠗⠑⠎⠊⠌⠨⠑"), + ("impedance", "⠊⠍⠏⠫⠨⠑"), + ("reactance", "⠗⠑⠁⠉⠞⠨⠑"), + ("variance", "⠧⠜⠊⠨⠑"), + ("tolerance", "⠞⠕⠇⠻⠨⠑"), + ("luminosity", "⠇⠥⠍⠔⠕⠎⠰⠽"), + ("reliability", "⠗⠑⠇⠊⠁⠃⠊⠇⠰⠽"), + ("velocity", "⠧⠑⠇⠕⠉⠰⠽"), + ("viscosity", "⠧⠊⠎⠉⠕⠎⠰⠽"), + ("precision", "⠏⠗⠑⠉⠊⠨⠝"), + ("tension", "⠞⠢⠨⠝"), + ("efficiency", "⠑⠖⠊⠉⠊⠢⠉⠽"), + ("frequency", "⠋⠗⠑⠟⠥⠢⠉⠽"), + ("valence", "⠧⠁⠇⠰⠑"), + ("stiffness", "⠌⠊⠖⠰⠎"), + ("thickness", "⠹⠊⠉⠅⠰⠎"), + ("displacement", "⠲⠏⠇⠁⠉⠑⠰⠞"), + ("concentration", "⠒⠉⠢⠞⠗⠁⠰⠝"), + ("consumption", "⠒⠎⠥⠍⠏⠰⠝"), + ("duration", "⠙⠥⠗⠁⠰⠝"), + ("elevation", "⠑⠇⠑⠧⠁⠰⠝"), + ("fraction", "⠋⠗⠁⠉⠰⠝"), + ("friction", "⠋⠗⠊⠉⠰⠝"), + ("inflation", "⠔⠋⠇⠁⠰⠝"), + ("iteration", "⠊⠞⠻⠁⠰⠝"), + ("population", "⠏⠕⠏⠥⠇⠁⠰⠝"), + ("position", "⠏⠕⠎⠊⠰⠝"), + ("potential", "⠏⠕⠞⠢⠞⠊⠁⠇"), + ("production", "⠏⠗⠕⠙⠥⠉⠰⠝"), + ("resolution", "⠗⠑⠎⠕⠇⠥⠰⠝"), + ("rotation", "⠗⠕⠞⠁⠰⠝"), + ("charge", "⠡⠜⠛⠑"), + ("chord", "⠡⠕⠗⠙"), + ("pitch", "⠏⠊⠞⠡"), + ("growth", "⠛⠗⠪⠹"), + ("head", "⠓⠂⠙"), + ("heat", "⠓⠂⠞"), + ("reach", "⠗⠂⠡"), + ("shear", "⠩⠑⠜"), + ("margin", "⠍⠜⠛⠔"), + ("salary", "⠎⠁⠇⠜⠽"), + ("offset", "⠷⠋⠎⠑⠞"), + ("income", "⠔⠉⠕⠍⠑"), + ("input", "⠔⠏⠥⠞"), + ("inventory", "⠔⠧⠢⠞⠕⠗⠽"), + ("current", "⠉⠥⠗⠗⠢⠞"), + ("error", "⠻⠗⠕⠗"), + ("leverage", "⠇⠐⠑⠁⠛⠑"), + ("temperature", "⠞⠑⠍⠏⠻⠁⠞⠥⠗⠑"), + ("turnover", "⠞⠥⠗⠝⠕⠧⠻"), + ("accuracy", "⠁⠒⠥⠗⠁⠉⠽"), + ("breadth", "⠃⠗⠂⠙⠹"), + ("strength", "⠌⠗⠢⠛⠹"), + ("spread", "⠎⠏⠗⠂⠙"), + ("discount", "⠲⠉⠨⠞"), + ("count", "⠉⠨⠞"), + ("version", "⠧⠻⠨⠝"), + ("inductance", "⠔⠙⠥⠉⠞⠨⠑"), + ("intensity", "⠔⠞⠢⠎⠰⠽"), + ("interval", "⠔⠞⠻⠧⠁⠇"), + ("perimeter", "⠏⠻⠊⠍⠑⠞⠻"), + ("probability", "⠏⠗⠕⠃⠁⠃⠊⠇⠰⠽"), + ("utility", "⠥⠞⠊⠇⠰⠽"), + ("validity", "⠧⠁⠇⠊⠙⠰⠽"), + ("parity", "⠏⠜⠰⠽"), + ("permeability", "⠏⠻⠍⠂⠃⠊⠇⠰⠽"), + ("proportion", "⠏⠗⠕⠏⠕⠗⠰⠝"), + ("uncertainty", "⠥⠝⠉⠻⠞⠁⠔⠞⠽"), + ("circumference", "⠉⠊⠗⠉⠥⠍⠋⠻⠰⠑"), + ("capacity", "⠉⠁⠏⠁⠉⠰⠽"), + ("deceleration", "⠙⠑⠉⠑⠇⠻⠁⠰⠝"), + ("dilution", "⠙⠊⠇⠥⠰⠝"), + ("enthalpy", "⠢⠹⠁⠇⠏⠽"), + ("entropy", "⠢⠞⠗⠕⠏⠽"), + ("equity", "⠑⠟⠥⠰⠽"), + ("expense", "⠑⠭⠏⠢⠎⠑"), + ("gradient", "⠛⠗⠁⠙⠊⠢⠞"), + ("headroom", "⠓⠂⠙⠗⠕⠕⠍"), + ("inertia", "⠔⠻⠞⠊⠁"), + ("latency", "⠇⠁⠞⠢⠉⠽"), + ("longitude", "⠇⠰⠛⠊⠞⠥⠙⠑"), + ("permittivity", "⠏⠻⠍⠊⠞⠞⠊⠧⠰⠽"), + ("period", "⠏⠻⠊⠕⠙"), + ("principal", "⠏⠗⠔⠉⠊⠏⠁⠇"), + ("sensitivity", "⠎⠢⠎⠊⠞⠊⠧⠰⠽"), + ("stress", "⠌⠗⠑⠎⠎"), + ("throughput", "⠐⠹⠏⠥⠞"), + ("thrust", "⠹⠗⠥⠌"), + ("wavelength", "⠺⠁⠧⠑⠇⠢⠛⠹"), + ("absorption", "⠁⠃⠎⠕⠗⠏⠰⠝"), + ("activation", "⠁⠉⠞⠊⠧⠁⠰⠝"), + ("activity", "⠁⠉⠞⠊⠧⠰⠽"), + ("admittance", "⠁⠙⠍⠊⠞⠞⠨⠑"), + ("affinity", "⠁⠖⠔⠰⠽"), + ("albedo", "⠁⠇⠃⠫⠕"), + ("aperture", "⠁⠏⠻⠞⠥⠗⠑"), + ("attenuation", "⠁⠞⠞⠢⠥⠁⠰⠝"), + ("average", "⠁⠧⠻⠁⠛⠑"), + ("bandwidth", "⠃⠯⠺⠊⠙⠹"), + ("baseline", "⠃⠁⠎⠑⠇⠔⠑"), + ("bearing", "⠃⠑⠜⠬"), + ("benchmark", "⠃⠢⠡⠍⠜⠅"), + ("boiling", "⠃⠕⠊⠇⠬"), + ("boundary", "⠃⠨⠙⠜⠽"), + ("cohesion", "⠉⠕⠓⠑⠨⠝"), + ("compliance", "⠉⠕⠍⠏⠇⠊⠨⠑"), + ("compression", "⠉⠕⠍⠏⠗⠑⠎⠨⠝"), + ("conductivity", "⠒⠙⠥⠉⠞⠊⠧⠰⠽"), + ("coverage", "⠉⠕⠧⠻⠁⠛⠑"), + ("damping", "⠙⠁⠍⠏⠬"), + ("deflection", "⠙⠑⠋⠇⠑⠉⠰⠝"), + ("departure", "⠙⠑⠐⠏⠥⠗⠑"), + ("depreciation", "⠙⠑⠏⠗⠑⠉⠊⠁⠰⠝"), + ("deviation", "⠙⠑⠧⠊⠁⠰⠝"), + ("diffusion", "⠙⠊⠖⠥⠨⠝"), + ("dispersion", "⠲⠏⠻⠨⠝"), + ("dissipation", "⠲⠎⠊⠏⠁⠰⠝"), + ("distortion", "⠲⠞⠕⠗⠰⠝"), + ("divergence", "⠙⠊⠧⠻⠛⠰⠑"), + ("downforce", "⠙⠪⠝⠿⠉⠑"), + ("elasticity", "⠑⠇⠁⠌⠊⠉⠰⠽"), + ("emissivity", "⠑⠍⠊⠎⠎⠊⠧⠰⠽"), + ("emission", "⠑⠍⠊⠎⠨⠝"), + ("erosion", "⠻⠕⠨⠝"), + ("evaporation", "⠑⠧⠁⠏⠕⠗⠁⠰⠝"), + ("expansion", "⠑⠭⠏⠁⠝⠨⠝"), + ("expectation", "⠑⠭⠏⠑⠉⠞⠁⠰⠝"), + ("extent", "⠑⠭⠞⠢⠞"), + ("feedback", "⠋⠑⠫⠃⠁⠉⠅"), + ("fidelity", "⠋⠊⠙⠑⠇⠰⠽"), + ("filament", "⠋⠊⠇⠁⠰⠞"), + ("hardness", "⠓⠜⠙⠰⠎"), + ("humidity", "⠓⠥⠍⠊⠙⠰⠽"), + ("hysteresis", "⠓⠽⠌⠻⠑⠎⠊⠎"), + ("illuminance", "⠊⠇⠇⠥⠍⠔⠨⠑"), + ("incidence", "⠔⠉⠊⠙⠰⠑"), + ("index", "⠔⠙⠑⠭"), + ("inflow", "⠔⠋⠇⠪"), + ("influx", "⠔⠋⠇⠥⠭"), + ("inhibition", "⠔⠓⠊⠃⠊⠰⠝"), + ("injection", "⠔⠚⠑⠉⠰⠝"), + ("irradiance", "⠊⠗⠗⠁⠙⠊⠨⠑"), + ("kinetic", "⠅⠔⠑⠞⠊⠉"), + ("leakage", "⠇⠂⠅⠁⠛⠑"), + ("luminance", "⠇⠥⠍⠔⠨⠑"), + ("maturity", "⠍⠁⠞⠥⠗⠰⠽"), + ("mean", "⠍⠂⠝"), + ("median", "⠍⠫⠊⠁⠝"), + ("melting", "⠍⠑⠇⠞⠬"), + ("minimum", "⠍⠔⠊⠍⠥⠍"), + ("mobility", "⠍⠕⠃⠊⠇⠰⠽"), + ("molarity", "⠍⠕⠇⠜⠰⠽"), + ("moisture", "⠍⠕⠊⠌⠥⠗⠑"), + ("opacity", "⠕⠏⠁⠉⠰⠽"), + ("oscillation", "⠕⠎⠉⠊⠇⠇⠁⠰⠝"), + ("outflow", "⠳⠞⠋⠇⠪"), + ("overlap", "⠕⠧⠻⠇⠁⠏"), + ("parallax", "⠏⠜⠁⠇⠇⠁⠭"), + ("parameter", "⠏⠜⠁⠍⠑⠞⠻"), + ("peak", "⠏⠂⠅"), + ("percentile", "⠏⠻⠉⠢⠞⠊⠇⠑"), + ("percentage", "⠏⠻⠉⠢⠞⠁⠛⠑"), + ("polarization", "⠏⠕⠇⠜⠊⠵⠁⠰⠝"), + ("propagation", "⠏⠗⠕⠏⠁⠛⠁⠰⠝"), + ("purity", "⠏⠥⠗⠰⠽"), + ("quality", "⠟⠥⠁⠇⠰⠽"), + ("quantity", "⠟⠥⠁⠝⠞⠰⠽"), + ("quenching", "⠟⠥⠢⠡⠬"), + ("quotient", "⠟⠥⠕⠞⠊⠢⠞"), + ("radiance", "⠗⠁⠙⠊⠨⠑"), + ("radiation", "⠗⠁⠙⠊⠁⠰⠝"), + ("reaction", "⠗⠑⠁⠉⠰⠝"), + ("reactivity", "⠗⠑⠁⠉⠞⠊⠧⠰⠽"), + ("redshift", "⠗⠫⠩⠊⠋⠞"), + ("reflectance", "⠗⠑⠋⠇⠑⠉⠞⠨⠑"), + ("reflection", "⠗⠑⠋⠇⠑⠉⠰⠝"), + ("refraction", "⠗⠑⠋⠗⠁⠉⠰⠝"), + ("reluctance", "⠗⠑⠇⠥⠉⠞⠨⠑"), + ("remanence", "⠗⠑⠍⠁⠝⠰⠑"), + ("resistivity", "⠗⠑⠎⠊⠌⠊⠧⠰⠽"), + ("resonance", "⠗⠑⠎⠕⠝⠨⠑"), + ("restitution", "⠗⠑⠌⠊⠞⠥⠰⠝"), + ("retardation", "⠗⠑⠞⠜⠙⠁⠰⠝"), + ("rigidity", "⠗⠊⠛⠊⠙⠰⠽"), + ("salinity", "⠎⠁⠇⠔⠰⠽"), + ("saturation", "⠎⠁⠞⠥⠗⠁⠰⠝"), + ("scalar", "⠎⠉⠁⠇⠜"), + ("scattering", "⠎⠉⠁⠞⠞⠻⠬"), + ("separation", "⠎⠑⠏⠜⠁⠰⠝"), + ("shrinkage", "⠩⠗⠔⠅⠁⠛⠑"), + ("similarity", "⠎⠊⠍⠊⠇⠜⠰⠽"), + ("battery", "⠃⠁⠞⠞⠻⠽"), + ("diameter", "⠙⠊⠁⠍⠑⠞⠻"), + ("epoch", "⠑⠏⠕⠡"), + ("gain", "⠛⠁⠔"), + ("jitter", "⠚⠊⠞⠞⠻"), + ("joule", "⠚⠳⠇⠑"), + ("momentum", "⠍⠕⠰⠞⠥⠍"), + ("shift", "⠩⠊⠋⠞"), + ("step", "⠌⠑⠏"), + ("stride", "⠌⠗⠊⠙⠑"), + ("zenith", "⠵⠢⠊⠹"), + ]; + const SHOULD_NOT_CONTRACT: &[&str] = &[ + "mass", "volume", "base", "rate", "drag", "drift", "gap", "lag", "loss", "pace", + "radius", "range", "ratio", "risk", "run", "scale", "size", "slope", "tax", "torque", + "capital", "impulse", "latitude", "magnitude", "premium", "pressure", "response", "return", + "trajectory", "voltage", "aspect", "backlog", "curvature", "dosage", "dose", "equilibrium", + "excess", "exposure", "fatigue", "hydraulic", "lifetime", "likelihood", "maximum", "metric", + "orbit", "osmosis", "pulse", "quanta", "quantum", "regret", "residual", "ripple", "root", + "rotor", "sag", "secant", "signal", "altitude", "amplitude", "angle", "bias", "bitrate", + "budget", "bulk", "decay", "deficit", "degree", "delay", "duty", "lift", "limit", "load", + "modulus", "payload", "phase", "price", "sample", "skew", "slack", "span", "supply", + "total", "value", "vector", "wage", "yield", + ]; + + for &(word, expected) in SHOULD_CONTRACT { + let got = contract_word(word); + assert_eq!(expected, got, "word '{word}'"); + assert_ne!(ascii_to_uncontracted_braille(word), got, "word '{word}' should contract"); + } + for &word in SHOULD_NOT_CONTRACT { + let got = contract_word(word); + let uncontracted = ascii_to_uncontracted_braille(word); + assert_eq!(uncontracted, got, "word '{word}' should not contract"); + } + } + + #[test] + #[ignore] + fn ueb_technical_word_contractions_probe_batch2() { + let try_contract = [ + "circumference", "capacity", "deceleration", "dilution", "enthalpy", "entropy", "equity", "expense", + "gradient", "headroom", "inertia", "latency", "longitude", "permittivity", "period", "principal", + "sensitivity", "stress", "throughput", "thrust", "wavelength", "absorption", "activation", "activity", + "admittance", "affinity", "albedo", "aperture", "attenuation", "average", "bandwidth", "baseline", + "bearing", "benchmark", "boiling", "boundary", "cohesion", "compliance", "compression", "conductivity", + "coverage", "damping", "deflection", "departure", "depreciation", "deviation", "diffusion", "dispersion", + "dissipation", "distortion", "divergence", "downforce", "elasticity", "emissivity", "emission", "erosion", + "evaporation", "expansion", "expectation", "extent", "feedback", "fidelity", "filament", "hardness", + "humidity", "hysteresis", "illuminance", "incidence", "index", "inflow", "influx", "inhibition", + "injection", "irradiance", "kinetic", "leakage", "luminance", "maturity", "mean", "median", "melting", + "minimum", "mobility", "molarity", "moisture", "opacity", "oscillation", "outflow", "overlap", "parallax", + "parameter", "peak", "percentile", "percentage", "polarization", "propagation", "purity", "quality", + "quantity", "quenching", "quotient", "radiance", "radiation", "reaction", "reactivity", "redshift", + "reflectance", "reflection", "refraction", "reluctance", "remanence", "resistivity", "resonance", + "restitution", "retardation", "rigidity", "salinity", "saturation", "scalar", "scattering", "separation", + "shrinkage", "similarity", "battery", "diameter", "epoch", "gain", "jitter", "joule", "momentum", "shift", + "step", "stride", "zenith", + ]; + let try_not = [ + "capital", "impulse", "latitude", "magnitude", "premium", "pressure", "response", "return", "trajectory", + "voltage", "aspect", "backlog", "curvature", "dosage", "dose", "equilibrium", "excess", "exposure", + "fatigue", "hydraulic", "lifetime", "likelihood", "maximum", "metric", "orbit", "osmosis", "pulse", + "quanta", "quantum", "regret", "residual", "ripple", "root", "rotor", "sag", "secant", "signal", + "altitude", "amplitude", "angle", "bias", "bitrate", "budget", "bulk", "decay", "deficit", "degree", + "delay", "duty", "lift", "limit", "load", "modulus", "payload", "phase", "price", "sample", "skew", + "slack", "span", "supply", "total", "value", "vector", "wage", "yield", + ]; + let mut out = String::new(); + for w in try_contract { + let c = contract_word(w); + out.push_str(&format!("(\"{w}\", \"{c}\"),\n")); + } + for w in try_not { + out.push_str(&format!("// no: {w}\n")); + } + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("notes/_batch2_goldens.txt"); + std::fs::write(path, out).unwrap(); + } + + #[test] + #[ignore] + fn ueb_technical_word_contractions_probe() { + let should_contract = [ + "force", "work", "time", "speed", "distance", "area", "height", "weight", "power", "energy", + "density", "gravity", "acceleration", "length", "width", "depth", "cost", "profit", "interest", "revenue", + "clearance", "conductance", "capacitance", "resistance", "impedance", "reactance", "variance", "tolerance", + "luminosity", "reliability", "velocity", "viscosity", "precision", "tension", "efficiency", "frequency", + "valence", "stiffness", "thickness", "displacement", "concentration", "consumption", "duration", "elevation", + "fraction", "friction", "inflation", "iteration", "population", "position", "potential", "production", + "resolution", "rotation", "charge", "chord", "pitch", "growth", "head", "heat", "reach", "shear", "margin", + "salary", "offset", "income", "input", "inventory", "current", "error", "leverage", "temperature", + "turnover", "accuracy", "breadth", "strength", "spread", "discount", "count", "version", "inductance", + "intensity", "interval", "perimeter", "probability", "utility", "validity", "parity", "permeability", + "proportion", "uncertainty", + ]; + let should_not = [ + "mass", "volume", "base", "rate", "drag", "drift", "gap", "lag", "loss", "pace", + "radius", "range", "ratio", "risk", "run", "scale", "size", "slope", "tax", "torque", + ]; + eprintln!("=== SHOULD CONTRACT ==="); + for w in should_contract { + let c = contract_word(w); + let u = ascii_to_uncontracted_braille(w); + eprintln!("(\"{w}\", \"{c}\"), // uncontracted={u} changed={}", c != u); + } + eprintln!("=== SHOULD NOT CONTRACT ==="); + for w in should_not { + let c = contract_word(w); + let u = ascii_to_uncontracted_braille(w); + eprintln!("{w}: contracted={c} uncontracted={u} ok={}", c == u); + } + } } diff --git a/tests/braille/UEB/iceb.rs b/tests/braille/UEB/iceb.rs index 3bd48b19b..70eb878f8 100644 --- a/tests/braille/UEB/iceb.rs +++ b/tests/braille/UEB/iceb.rs @@ -1,7 +1,8 @@ // UEB tests for the basic mathml tags -// Initial tests are from BANA guidelines, mostly about initial chars for code switching -// http://www.brailleauthority.org/ueb/ueb_math_guidance/final_for_posting_ueb_math_guidance_may_2019_102419.pdf -// These tests start with "bana_" +// BANA guidance (2026; replaces 2019 provisional): +// https://www.brailleauthority.org/sites/default/files/2026-07/Guidance%20on%20Transcribing%20Math%20and%20Science%20in%20UEB%202026.pdf +// Older `bana_*` names map from 2019 section numbers where still valid; `bana2026_*` are new 2026 examples. +// Grade 1 indicators follow ICEB GTM §1.7 (adopted by BANA 2026). // // Many come from (refer to) https://iceb.org/guidelines_for_technical_material_2014.pdf // For example, "fraction_6_1_1" is a fraction example from section 6.1, and is the first example there. @@ -10,7 +11,8 @@ use anyhow::Result; #[test] fn bana_2_1() -> Result<()> { - let expr = "6=1×2×3 + // BANA 2026 Ex 2-1 (spacing of signs) + let expr = "6=1×2×3 =1+2+3"; test_braille("UEB", expr, "⠼⠋⠀⠐⠶⠀⠼⠁⠐⠦⠼⠃⠐⠦⠼⠉⠀⠐⠶⠀⠼⠁⠐⠖⠼⠃⠐⠖⠼⠉")?; return Ok(()); @@ -19,6 +21,7 @@ fn bana_2_1() -> Result<()> { #[test] fn bana_5_1() -> Result<()> { + // BANA 2026 Ex 5-1 let expr = "x+y=6"; test_braille("UEB", expr, "⠭⠐⠖⠽⠀⠐⠶⠀⠼⠋")?; return Ok(()); @@ -27,6 +30,7 @@ fn bana_5_1() -> Result<()> { #[test] fn bana_5_2() -> Result<()> { + // BANA 2026 Ex 5-7 let expr = "x2+y2=C"; test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠖⠽⠔⠼⠃⠀⠐⠶⠀⠰⠠⠉")?; return Ok(()); @@ -35,6 +39,7 @@ fn bana_5_2() -> Result<()> { #[test] fn bana_5_3() -> Result<()> { + // BANA 2026 Ex 5-10 let expr = "ab+cd"; test_braille("UEB", expr, "⠰⠰⠷⠁⠨⠌⠃⠾⠐⠖⠷⠉⠨⠌⠙⠾")?; return Ok(()); @@ -43,26 +48,27 @@ fn bana_5_3() -> Result<()> { #[test] fn bana_5_4() -> Result<()> { - let expr = "an×am= + // BANA 2026 Ex 5-14 (GTM 1.7: word indicator per sequence, not passage) + let expr = "an×am= an+m"; - test_braille("UEB", expr, "⠰⠰⠰⠁⠔⠝⠐⠦⠁⠔⠍⠀⠐⠶⠀⠁⠔⠣⠝⠐⠖⠍⠜⠰⠄")?; + test_braille("UEB", expr, "⠰⠰⠁⠔⠝⠐⠦⠁⠔⠍⠀⠐⠶⠀⠰⠰⠁⠔⠣⠝⠐⠖⠍⠜")?; return Ok(()); } #[test] fn bana_5_5() -> Result<()> { - let expr = "logxy"; - // BANA example contradicts GTM 9.2 that says don't use a space after a function name if there is an intervening indicator. - // Corrected: removed the space and the G1 indicator needed if a space were inserted - test_braille("UEB", expr, "⠰⠰⠇⠕⠛⠢⠭⠽")?; + // BANA 2026 Ex 5-8 (replaces 2019 log_x y example) + let expr = "log2x+logx"; + test_braille("UEB", expr, "⠇⠕⠛⠰⠢⠼⠃⠭⠐⠖⠇⠕⠛⠀⠰⠭")?; return Ok(()); } #[test] fn bana_5a_1() -> Result<()> { - let expr = "100°F"; + // Former 2019 §5(a) units example (dropped from BANA 2026); kept as unit-spacing practice + let expr = "100°F"; test_braille("UEB", expr, "⠼⠁⠚⠚⠘⠚⠠⠋")?; return Ok(()); @@ -70,7 +76,17 @@ fn bana_5a_1() -> Result<()> { #[test] fn bana_5a_1_baseline() -> Result<()> { - let expr = "100°F"; + // Former 2019 §5(a) units example (dropped from BANA 2026); kept as unit-spacing practice + let expr = "100°F"; + test_braille("UEB", expr, "⠼⠁⠚⠚⠘⠚⠠⠋")?; + return Ok(()); + +} + +#[test] +fn bana_5a_1_baseline_unit() -> Result<()> { + // Same as bana_5a_1_baseline; unit marked via data-intent-property + let expr = "100°F"; test_braille("UEB", expr, "⠼⠁⠚⠚⠘⠚⠠⠋")?; return Ok(()); @@ -78,22 +94,43 @@ fn bana_5a_1_baseline() -> Result<()> { #[test] fn bana_5a_2() -> Result<()> { + // Former 2019 §5(a) units example (dropped from BANA 2026); kept as unit-spacing practice let expr = "25 km2"; test_braille("UEB", expr, "⠼⠃⠑⠀⠅⠍⠰⠔⠼⠃")?; return Ok(()); } +#[test] +fn bana_5a_2_unit() -> Result<()> { + // Same as bana_5a_2; unit marked via data-intent-property + let expr = "25 km2"; + test_braille("UEB", expr, "⠼⠃⠑⠀⠅⠍⠰⠔⠼⠃")?; + return Ok(()); + +} + #[test] fn bana_5a_2_mtext() -> Result<()> { + // Former 2019 §5(a) units example (dropped from BANA 2026); kept as unit-spacing practice let expr = "25 km2"; test_braille("UEB", expr, "⠼⠃⠑⠀⠅⠍⠰⠔⠼⠃")?; return Ok(()); } +#[test] +fn bana_5a_2_mtext_unit() -> Result<()> { + // Same as bana_5a_2_mtext; unit marked via data-intent-property + let expr = "25 km2"; + test_braille("UEB", expr, "⠼⠃⠑⠀⠅⠍⠰⠔⠼⠃")?; + return Ok(()); + +} + #[test] fn bana_5a_3() -> Result<()> { + // Former 2019 §5(a) units example (dropped from BANA 2026); kept as unit-spacing practice let expr = "6 m  s-1"; test_braille("UEB", expr, "⠼⠋⠀⠰⠍⠀⠰⠰⠎⠔⠣⠐⠤⠼⠁⠜")?; @@ -101,11 +138,183 @@ fn bana_5a_3() -> Result<()> { } +#[test] +fn bana_5a_3_unit() -> Result<()> { + // Same as bana_5a_3; unit marked via data-intent-property + let expr = "6 m  + s-1"; + test_braille("UEB", expr, "⠼⠋⠀⠰⠍⠀⠰⠰⠎⠔⠣⠐⠤⠼⠁⠜")?; + return Ok(()); + +} + #[test] fn bana_6_1() -> Result<()> { + // Punctuation before G1 terminator (BANA 2026 §5.4 / GTM 1.7.3b note) let expr = "x+y=z =t2."; - test_braille("UEB", expr, "⠰⠰⠰⠭⠐⠖⠽⠀⠐⠶⠀⠵⠀⠐⠶⠀⠞⠔⠼⠃⠲⠰⠄")?; + test_braille("UEB", expr, "⠭⠐⠖⠽⠀⠐⠶⠀⠰⠵⠀⠐⠶⠀⠞⠰⠔⠼⠃⠲")?; + return Ok(()); + +} + +// --- BANA 2026 examples not covered by older bana_* tests --- + +#[test] +fn bana2026_2_2() -> Result<()> { + // BANA 2026 Ex 2-2: spaced operation signs for beginning readers + let expr = "6-3=_"; + test_braille_prefs("UEB", vec![("UseSpacesAroundAllOperators", "true")], expr, "⠼⠋⠀⠐⠤⠀⠼⠉⠀⠐⠶⠀⠨⠤")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_2() -> Result<()> { + // BANA 2026 Ex 5-2: words with contractions in a technical expression + let expr = "Surface area=2lh+2lw+2wh"; + test_braille("UEB", expr, "⠠⠎⠥⠗⠋⠁⠉⠑⠀⠜⠑⠁⠀⠐⠶⠀⠼⠃⠇⠓⠐⠖⠼⠃⠇⠺⠐⠖⠼⠃⠺⠓")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_3() -> Result<()> { + // BANA 2026 Ex 5-3 + let expr = "NaCl+H2O"; + test_braille("UEB", expr, "⠠⠝⠁⠠⠉⠇⠐⠖⠠⠓⠰⠢⠼⠃⠠⠕")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_4() -> Result<()> { + // BANA 2026 Ex 5-4 + let expr = "5x2y3z"; + test_braille("UEB", expr, "⠰⠷⠼⠑⠭⠔⠼⠃⠨⠌⠽⠔⠼⠉⠵⠾")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_5() -> Result<()> { + // BANA 2026 Ex 5-5: 1×3 matrix and column (linearized like other matrix_* tests) + let expr = r#" + (abc) + ( + -1 + 2 + -3 + ) + "#; + test_braille("UEB", expr, "⠠⠐⠣⠁⠀⠰⠃⠀⠉⠠⠐⠜⠠⠐⠣⠐⠤⠼⠁⠠⠐⠜⠸⠀⠠⠐⠣⠼⠃⠠⠐⠜⠸⠀⠠⠐⠣⠐⠤⠼⠉⠠⠐⠜")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_6() -> Result<()> { + // BANA 2026 Ex 5-6 + let expr = r#" + (ab) + (-x2y) + "#; + test_braille("UEB", expr, "⠠⠐⠣⠁⠀⠃⠠⠐⠜⠠⠐⠣⠐⠤⠭⠠⠐⠜⠸⠀⠠⠐⠣⠼⠃⠽⠠⠐⠜")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_9_slope() -> Result<()> { + // BANA 2026 Ex 5-9 (math fragments): slope 3/5 and parallel lines l₁ ∥ l₂ + let expr = "l1∥l2"; + test_braille("UEB", expr, "⠇⠰⠢⠼⠁⠀⠼⠇⠀⠇⠰⠢⠼⠃")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_11() -> Result<()> { + // BANA 2026 Ex 5-11 + let expr = "ab"; + test_braille("UEB", expr, "⠰⠰⠷⠁⠨⠌⠃⠾")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_12() -> Result<()> { + // BANA 2026 Ex 5-12: vector AB with arrow + let expr = "AB→"; + test_braille("UEB", expr, "⠰⠰⠣⠠⠠⠁⠃⠜⠘⠱")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_13() -> Result<()> { + // BANA 2026 Ex 5-13 + let expr = "x-1=1x"; + test_braille("UEB", expr, "⠰⠰⠭⠔⠣⠐⠤⠼⠁⠜⠀⠐⠶⠀⠰⠷⠼⠁⠨⠌⠭⠾")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_15() -> Result<()> { + // BANA 2026 Ex 5-15: all-caps print uses capital passage (⠠⠠⠠ … ⠠⠄) inside G1 passage + let expr = "P=VI=I2R=V2R"; + test_braille("UEB", expr, "⠰⠰⠰⠠⠠⠠⠏⠀⠐⠶⠀⠧⠊⠀⠐⠶⠀⠊⠔⠼⠃⠗⠀⠐⠶⠀⠷⠧⠔⠼⠃⠨⠌⠗⠾⠠⠄⠰⠄")?; + return Ok(()); + +} + +#[test] +fn bana2026_5_16() -> Result<()> { + // BANA 2026 Ex 5-16: CaCO₃ ⇄ CaO + CO₂ + let expr = "CaCO3 + ⇄ + CaO+ + CO2"; + test_braille("UEB", expr, "⠰⠰⠰⠠⠉⠁⠠⠉⠠⠕⠢⠼⠉⠀⠳⠕⠻⠳⠪⠀⠠⠉⠁⠠⠕⠐⠖⠠⠉⠠⠕⠢⠼⠃⠰⠄")?; + return Ok(()); + +} + +#[test] +fn bana2026_7_1() -> Result<()> { + // BANA 2026 Ex 7-1: V = lim Δx→0 Σ f(xᵢ)Δx (single-line) + let expr = r#"V= + limΔx→0 + ∑i + f(xi)Δx + "#; + test_braille("UEB", expr, "⠰⠠⠧⠀⠐⠶⠀⠰⠰⠇⠊⠍⠨⠢⠣⠠⠨⠙⠭⠳⠕⠼⠚⠜⠠⠨⠎⠨⠢⠊⠋⠐⠣⠭⠢⠊⠐⠜⠠⠨⠙⠭")?; + return Ok(()); + +} + +#[test] +fn bana2026_7_2() -> Result<()> { + // BANA 2026 Ex 7-2: first step of radical simplification chain + let expr = r#"x= + 36-4(9)+3(12)18 + "#; + test_braille("UEB", expr, "⠰⠭⠀⠐⠶⠀⠰⠰⠷⠩⠼⠉⠋⠐⠤⠼⠙⠐⠣⠼⠊⠐⠜⠐⠖⠼⠉⠐⠣⠼⠁⠃⠐⠜⠬⠨⠌⠼⠁⠓⠾")?; + return Ok(()); + +} + +#[test] +fn bana2026_7_3() -> Result<()> { + // BANA 2026 Ex 7-3 + let expr = r#"x= + + 54+7-111×2+8000+1000 + -42 + + "#; + test_braille("UEB", expr, "⠰⠭⠀⠐⠶⠀⠰⠷⠼⠑⠙⠐⠖⠼⠛⠐⠤⠼⠁⠁⠁⠐⠦⠼⠃⠐⠖⠼⠓⠚⠚⠚⠐⠖⠼⠁⠚⠚⠚⠨⠌⠐⠤⠼⠙⠃⠾")?; return Ok(()); } @@ -167,7 +376,7 @@ fn grade1_1_7_2() -> Result<()> { #[test] fn grade1_1_7_3_1() -> Result<()> { let expr = " - 3x−4y+ + 3x−4y+ y2 = x2 @@ -193,13 +402,614 @@ fn grade1_1_7_3_2() -> Result<()> { #[test] fn grade1_1_7_4() -> Result<()> { let expr = " - (y−x2) + (y−x2) "; test_braille("UEB", expr, "⠰⠰⠩⠐⠣⠽⠐⠤⠭⠔⠼⠃⠐⠜⠬")?; return Ok(()); } +// Additional examples from GTM 1.7 (July 2025): +// https://iceb.org/wp-content/uploads/2026/02/GTM-1.7-Grade-1-Indicators-Approved.pdf +// Ordinary surrounding text is omitted; braille is the preferred math from the spec. + +#[test] +fn grade1_1_7_3_a_1() -> Result<()> { + let expr = "x2"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠃")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_3_a_2() -> Result<()> { + let expr = "x→∞"; + test_braille("UEB", expr, "⠰⠭⠀⠰⠳⠕⠀⠼⠿")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_3_a_3() -> Result<()> { + let expr = "xy"; + test_braille("UEB", expr, "⠰⠰⠷⠭⠨⠌⠽⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_3_b_1() -> Result<()> { + let expr = "xa=xb + =xc"; + test_braille("UEB", expr, "⠰⠰⠰⠭⠢⠁⠀⠐⠶⠀⠭⠢⠃⠀⠐⠶⠀⠭⠢⠉⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_3_b_2() -> Result<()> { + let expr = "y=x2-4; +  y=x2-2x; +  y=x-x2."; + test_braille("UEB", expr, "⠰⠰⠰⠽⠀⠐⠶⠀⠭⠔⠼⠃⠐⠤⠼⠙⠆⠀⠽⠀⠐⠶⠀⠭⠔⠼⠃⠐⠤⠼⠃⠭⠆⠀⠽⠀⠐⠶⠀⠭⠐⠤⠭⠔⠼⠃⠲⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_4_1() -> Result<()> { + let expr = "x2-x-2 + =0"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠭⠐⠤⠼⠃⠀⠐⠶⠀⠼⠚")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_4_2() -> Result<()> { + let expr = "x2-4x-3 + =0"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠼⠙⠭⠐⠤⠼⠉⠀⠐⠶⠀⠼⠚")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_4_3() -> Result<()> { + let expr = "x2-1=0"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠼⠁⠀⠐⠶⠀⠼⠚")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_5_a() -> Result<()> { + let expr = "speed= + distancetime"; + // GTM 1.7.5(a) (2025): two grade 1 symbol indicators so the words stay contracted. + // After the fraction open, "dis" is not a word start (RUEB 10.6.2), so st+ance: ⠙⠊⠌⠨⠑. + test_braille("UEB", expr, "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠷⠙⠊⠌⠨⠑⠨⠌⠐⠞⠰⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_5_b() -> Result<()> { + let expr = "luminositysun"; + test_braille("UEB", expr, "⠰⠰⠇⠥⠍⠊⠝⠕⠎⠊⠞⠽⠢⠣⠎⠥⠝⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_5_c() -> Result<()> { + let expr = "speed= + distancetime= + 30,000 m + 60 s= + 500 m/s"; + test_braille("UEB", expr, "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠰⠰⠷⠙⠊⠎⠞⠁⠝⠉⠑⠨⠌⠞⠊⠍⠑⠾⠀⠐⠶⠀⠷⠼⠉⠚⠂⠚⠚⠚⠀⠍⠨⠌⠼⠋⠚⠀⠎⠾⠀⠐⠶⠀⠼⠑⠚⠚⠀⠍⠸⠌⠎⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_5_c_unit() -> Result<()> { + // Same as grade1_1_7_5_c; unit marked via data-intent-property + let expr = "speed= + distancetime= + 30,000 m + 60 s= + 500 m/s"; + test_braille("UEB", expr, "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠰⠰⠷⠙⠊⠎⠞⠁⠝⠉⠑⠨⠌⠞⠊⠍⠑⠾⠀⠐⠶⠀⠷⠼⠉⠚⠂⠚⠚⠚⠀⠍⠨⠌⠼⠋⠚⠀⠎⠾⠀⠐⠶⠀⠼⠑⠚⠚⠀⠍⠸⠌⠎⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_1() -> Result<()> { + let expr = "y=x"; + test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠰⠭")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_2() -> Result<()> { + let expr = "25=5"; + test_braille("UEB", expr, "⠰⠩⠼⠃⠑⠬⠀⠐⠶⠀⠼⠑")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_3() -> Result<()> { + let expr = "x=7"; + test_braille("UEB", expr, "⠰⠰⠩⠭⠬⠀⠐⠶⠀⠼⠛")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_4() -> Result<()> { + let expr = "ms + -1"; + test_braille("UEB", expr, "⠰⠰⠍⠎⠔⠣⠐⠤⠼⠁⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_4_unit() -> Result<()> { + // Same as grade1_1_7_9_4; unit marked via data-intent-property + let expr = "ms + -1"; + test_braille("UEB", expr, "⠰⠰⠍⠎⠔⠣⠐⠤⠼⠁⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_5() -> Result<()> { + let expr = "y=x2"; + // GTM 1.7.9 (July 2025): one symbol indicator; numeric mode covers the closer. + test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠰⠷⠭⠨⠌⠼⠃⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_6() -> Result<()> { + let expr = "y=x22"; + test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠰⠰⠷⠭⠔⠼⠃⠨⠌⠼⠃⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_7() -> Result<()> { + let expr = "xy=cd"; + test_braille("UEB", expr, "⠰⠰⠷⠭⠨⠌⠽⠾⠀⠐⠶⠀⠰⠰⠷⠉⠨⠌⠙⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_8() -> Result<()> { + let expr = "(x,y)"; + test_braille("UEB", expr, "⠐⠣⠰⠭⠂⠀⠰⠽⠐⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_9() -> Result<()> { + let expr = "(xi,yi)"; + test_braille("UEB", expr, "⠐⠣⠭⠰⠢⠊⠂⠀⠽⠰⠢⠊⠐⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_10() -> Result<()> { + let expr = "(xi2, + yi2)"; + test_braille("UEB", expr, "⠰⠰⠐⠣⠭⠢⠊⠔⠼⠃⠂⠀⠰⠰⠽⠢⠊⠔⠼⠃⠐⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_11() -> Result<()> { + let expr = "B¯"; + test_braille("UEB", expr, "⠠⠃⠰⠱")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_12() -> Result<()> { + let expr = "M↔"; + test_braille("UEB", expr, "⠠⠍⠨⠔⠰⠳⠺⠗⠕")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_13() -> Result<()> { + let expr = "AZ + ↔"; + test_braille("UEB", expr, "⠰⠰⠣⠠⠠⠁⠵⠜⠨⠔⠳⠺⠗⠕")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_14() -> Result<()> { + let expr = "1327Al"; + test_braille("UEB", expr, "⠰⠢⠼⠁⠉⠔⠼⠃⠛⠠⠁⠇")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_15() -> Result<()> { + let expr = "O2-"; + test_braille("UEB", expr, "⠰⠰⠠⠕⠔⠣⠼⠃⠐⠤⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_16() -> Result<()> { + let expr = "2H2 + (g)+ + O2 + (g)→ + 2H2 + O(l)"; + test_braille("UEB", expr, "⠼⠃⠠⠓⠢⠼⠃⠐⠣⠛⠐⠜⠐⠖⠠⠕⠢⠼⠃⠐⠣⠛⠐⠜⠀⠰⠳⠕⠀⠼⠃⠠⠓⠢⠼⠃⠠⠕⠐⠣⠇⠐⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_17() -> Result<()> { + let expr = "y=mx+c"; + test_braille("UEB", expr, "⠰⠽⠀⠐⠶⠀⠍⠭⠐⠖⠉")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_18() -> Result<()> { + let expr = "A=πr2"; + test_braille("UEB", expr, "⠠⠁⠀⠐⠶⠀⠨⠏⠗⠰⠔⠼⠃")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_19() -> Result<()> { + let expr = "E=mc2"; + test_braille("UEB", expr, "⠰⠠⠑⠀⠐⠶⠀⠍⠉⠰⠔⠼⠃")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_20() -> Result<()> { + let expr = "x2-2x+1 + =(x-1)2"; + test_braille("UEB", expr, "⠭⠰⠔⠼⠃⠐⠤⠼⠃⠭⠐⠖⠼⠁⠀⠐⠶⠀⠐⠣⠭⠐⠤⠼⠁⠐⠜⠔⠼⠃")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_21() -> Result<()> { + let expr = "xn=1+ + -1nn"; + test_braille("UEB", expr, "⠭⠰⠢⠝⠀⠐⠶⠀⠼⠁⠐⠖⠷⠐⠤⠼⠁⠔⠝⠨⠌⠝⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_22() -> Result<()> { + let expr = "xn= + 2nn2"; + test_braille("UEB", expr, "⠭⠰⠢⠝⠀⠐⠶⠀⠰⠷⠼⠃⠔⠝⠨⠌⠝⠔⠼⠃⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_23() -> Result<()> { + let expr = "x= + + −b± + b2−4ac + + 2a + + "; + test_braille("UEB", expr, "⠰⠭⠀⠐⠶⠀⠰⠰⠷⠐⠤⠃⠸⠖⠩⠃⠔⠼⠃⠐⠤⠼⠙⠰⠁⠉⠬⠨⠌⠼⠃⠰⠁⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_24() -> Result<()> { + let expr = "xsinθ"; + test_braille("UEB", expr, "⠰⠰⠭⠩⠎⠊⠝⠨⠹⠬")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_25() -> Result<()> { + let expr = "Version1b"; + test_braille("UEB", expr, "⠰⠰⠠⠧⠑⠗⠎⠊⠕⠝⠔⠣⠼⠁⠰⠃⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_26() -> Result<()> { + let expr = "W=Fs"; + test_braille("UEB", expr, "⠰⠠⠺⠀⠐⠶⠀⠠⠋⠎")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_27() -> Result<()> { + let expr = "work=force×distance"; + test_braille("UEB", expr, "⠐⠺⠀⠐⠶⠀⠿⠉⠑⠐⠦⠙⠊⠌⠨⠑")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_28() -> Result<()> { + let expr = "∴force= + workdistance"; + test_braille("UEB", expr, "⠰⠠⠡⠀⠿⠉⠑⠀⠐⠶⠀⠰⠷⠐⠺⠨⠌⠙⠊⠌⠨⠑⠰⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_29() -> Result<()> { + let expr = "acceleration= + Δspeed + Δtime"; + test_braille("UEB", expr, "⠁⠒⠑⠇⠻⠁⠰⠝⠀⠐⠶⠀⠰⠷⠠⠨⠙⠎⠏⠑⠫⠨⠌⠠⠨⠙⠐⠞⠰⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_30() -> Result<()> { + let expr = "frequency=1time"; + test_braille("UEB", expr, "⠋⠗⠑⠟⠥⠢⠉⠽⠀⠐⠶⠀⠰⠷⠼⠁⠨⠌⠞⠊⠍⠑⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_31() -> Result<()> { + let expr = "frequency=1/time"; + test_braille("UEB", expr, "⠋⠗⠑⠟⠥⠢⠉⠽⠀⠐⠶⠀⠼⠁⠸⠌⠞⠊⠍⠑")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_32() -> Result<()> { + let expr = "Vcone= + 13π + r2h"; + test_braille("UEB", expr, "⠰⠰⠠⠧⠢⠣⠉⠕⠝⠑⠜⠀⠐⠶⠀⠼⠁⠌⠉⠨⠏⠗⠔⠼⠃⠰⠓")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_33() -> Result<()> { + let expr = "V=∫π + y2dx"; + test_braille("UEB", expr, "⠰⠠⠧⠀⠐⠶⠀⠰⠰⠮⠨⠏⠽⠔⠼⠃⠰⠙⠭")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_34() -> Result<()> { + let expr = "period=2π + + lcosα + g + "; + test_braille("UEB", expr, "⠏⠻⠊⠕⠙⠀⠐⠶⠀⠼⠃⠨⠏⠩⠷⠇⠀⠰⠰⠉⠕⠎⠨⠁⠨⠌⠛⠾⠬")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_35() -> Result<()> { + let expr = "Assume g=9.81  + ms + -2"; + test_braille("UEB", expr, "⠠⠁⠎⠎⠥⠍⠑⠀⠰⠛⠀⠐⠶⠀⠼⠊⠲⠓⠁⠀⠰⠰⠍⠎⠔⠣⠐⠤⠼⠃⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_35_unit() -> Result<()> { + // Same as grade1_1_7_9_35; unit marked via data-intent-property + let expr = "Assume g=9.81  + ms + -2"; + test_braille("UEB", expr, "⠠⠁⠎⠎⠥⠍⠑⠀⠰⠛⠀⠐⠶⠀⠼⠊⠲⠓⠁⠀⠰⠰⠍⠎⠔⠣⠐⠤⠼⠃⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_36() -> Result<()> { + let expr = "v1=x  + ms + -1"; + test_braille("UEB", expr, "⠰⠰⠰⠧⠢⠼⠁⠀⠐⠶⠀⠭⠀⠍⠎⠔⠣⠐⠤⠼⠁⠜⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_36_unit() -> Result<()> { + // Same as grade1_1_7_9_36; unit marked via data-intent-property + let expr = "v1=x  + ms + -1"; + test_braille("UEB", expr, "⠰⠰⠰⠧⠢⠼⠁⠀⠐⠶⠀⠭⠀⠍⠎⠔⠣⠐⠤⠼⠁⠜⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_37() -> Result<()> { + let expr = "P= + t,u,v"; + test_braille("UEB", expr, "⠰⠰⠰⠠⠏⠀⠐⠶⠀⠸⠣⠞⠂⠀⠥⠂⠀⠧⠸⠜⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_38() -> Result<()> { + let expr = "xa=xb + =xc=xd"; + test_braille("UEB", expr, "⠰⠰⠰⠭⠢⠁⠀⠐⠶⠀⠭⠢⠃⠀⠐⠶⠀⠭⠢⠉⠀⠐⠶⠀⠭⠢⠙⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_39() -> Result<()> { + let expr = "x2×x3 + =x2+3 + =x5"; + test_braille("UEB", expr, "⠰⠰⠰⠭⠔⠼⠃⠐⠦⠭⠔⠼⠉⠀⠐⠶⠀⠭⠔⠣⠼⠃⠐⠖⠼⠉⠜⠀⠐⠶⠀⠭⠔⠼⠑⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_40() -> Result<()> { + let expr = "am×an + =am+n"; + test_braille("UEB", expr, "⠰⠰⠁⠔⠍⠐⠦⠁⠔⠝⠀⠐⠶⠀⠰⠰⠁⠔⠣⠍⠐⠖⠝⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_41() -> Result<()> { + let expr = "(am)n + =amn"; + test_braille("UEB", expr, "⠰⠰⠐⠣⠁⠔⠍⠐⠜⠔⠝⠀⠐⠶⠀⠰⠰⠁⠔⠣⠍⠝⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_42() -> Result<()> { + let expr = "aman + =am-n"; + test_braille("UEB", expr, "⠰⠰⠷⠁⠔⠍⠨⠌⠁⠔⠝⠾⠀⠐⠶⠀⠰⠰⠁⠔⠣⠍⠐⠤⠝⠜")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_43() -> Result<()> { + let expr = "a-m + =1am"; + test_braille("UEB", expr, "⠰⠰⠁⠔⠣⠐⠤⠍⠜⠀⠐⠶⠀⠰⠷⠼⠁⠨⠌⠁⠔⠍⠾")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_44() -> Result<()> { + let expr = "a0=1"; + test_braille("UEB", expr, "⠁⠰⠔⠼⠚⠀⠐⠶⠀⠼⠁")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_45() -> Result<()> { + let expr = "a1n + =an"; + test_braille("UEB", expr, "⠰⠰⠁⠔⠷⠼⠁⠨⠌⠝⠾⠀⠐⠶⠀⠰⠰⠩⠔⠝⠁⠬")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_46() -> Result<()> { + let expr = "B→C"; + test_braille("UEB", expr, "⠰⠰⠰⠠⠃⠀⠳⠕⠀⠠⠉⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_47() -> Result<()> { + let expr = "x+y=5"; + test_braille("UEB", expr, "⠭⠐⠖⠽⠀⠐⠶⠀⠼⠑")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_48() -> Result<()> { + let expr = "CH4 + +4Cl2 + → + CCl4 + +4HCl"; + test_braille("UEB", expr, "⠰⠰⠰⠠⠉⠠⠓⠢⠼⠙⠐⠖⠼⠙⠠⠉⠇⠢⠼⠃⠀⠳⠕⠀⠠⠉⠠⠉⠇⠢⠼⠙⠐⠖⠼⠙⠠⠓⠠⠉⠇⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_49() -> Result<()> { + let expr = "614C + → + 714N + + + -10β"; + test_braille("UEB", expr, "⠰⠰⠰⠢⠼⠋⠔⠼⠁⠙⠠⠉⠀⠳⠕⠀⠢⠼⠛⠔⠼⠁⠙⠠⠝⠐⠖⠢⠣⠐⠤⠼⠁⠜⠔⠼⠚⠨⠃⠰⠄")?; + return Ok(()); + +} + +#[test] +fn grade1_1_7_9_50() -> Result<()> { + let expr = "reliability= + number of faultstotal number of items + =p"; + test_braille("UEB", expr, "⠰⠰⠰⠗⠑⠇⠊⠁⠃⠊⠇⠊⠞⠽⠀⠐⠶⠀⠷⠝⠥⠍⠃⠑⠗⠀⠕⠋⠀⠋⠁⠥⠇⠞⠎⠨⠌⠞⠕⠞⠁⠇⠀⠝⠥⠍⠃⠑⠗⠀⠕⠋⠀⠊⠞⠑⠍⠎⠾⠀⠐⠶⠀⠏⠰⠄")?; + return Ok(()); + +} + #[test] fn number_2_1_2() -> Result<()> { let expr = "3,000"; @@ -313,7 +1123,7 @@ fn bold_2_7_3() -> Result<()> { fn signs_2_10_2() -> Result<()> { let expr = "$0.30,  30c  - or 30¢"; + or 30¢"; test_braille("UEB", expr, "⠈⠎⠼⠚⠲⠉⠚⠂⠀⠼⠉⠚⠰⠉⠀⠕⠗⠀⠼⠉⠚⠈⠉")?; return Ok(()); @@ -323,7 +1133,7 @@ fn signs_2_10_2() -> Result<()> { fn signs_2_10_5() -> Result<()> { let expr = "1 ft 6 in  or  - 1′ 6′′"; + 1′ 6′′"; test_braille("UEB", expr, "⠼⠁⠀⠋⠞⠀⠼⠋⠀⠔⠀⠕⠗⠀⠼⠁⠶⠀⠼⠋⠶⠶")?; return Ok(()); @@ -331,8 +1141,8 @@ fn signs_2_10_5() -> Result<()> { #[test] fn signs_2_10_8() -> Result<()> { - let expr = "0°C or -  32°F"; + let expr = "0°C or +  32°F"; test_braille("UEB", expr, "⠼⠚⠘⠚⠠⠉⠀⠕⠗⠀⠼⠉⠃⠘⠚⠠⠋")?; return Ok(()); @@ -340,9 +1150,9 @@ fn signs_2_10_8() -> Result<()> { #[test] fn signs_2_10_16() -> Result<()> { - let expr = "1 Å= + let expr = "1 Å= 110,000  - μ"; + μ"; test_braille("UEB", expr, "⠼⠁⠀⠠⠘⠫⠁⠀⠐⠶⠀⠼⠁⠌⠁⠚⠂⠚⠚⠚⠀⠨⠍")?; return Ok(()); @@ -384,7 +1194,7 @@ fn expr_3_1_2() -> Result<()> { #[test] fn expr_3_1_3() -> Result<()> { - let expr = "3×5=5×3=15"; + let expr = "3×5=5×3=15"; test_braille("UEB", expr, "⠼⠉⠐⠦⠼⠑⠀⠐⠶⠀⠼⠑⠐⠦⠼⠉⠀⠐⠶⠀⠼⠁⠑")?; return Ok(()); @@ -393,7 +1203,7 @@ fn expr_3_1_3() -> Result<()> { #[test] fn expr_3_1_6() -> Result<()> { // example includes spaces, so does the MathML (from WIRIS) - let expr = "5.72 m÷10= + let expr = "5.72 m÷10= 57.2 cm"; test_braille("UEB", expr, "⠼⠑⠲⠛⠃⠀⠍⠐⠌⠼⠁⠚⠀⠐⠶⠀⠼⠑⠛⠲⠃⠀⠉⠍")?; return Ok(()); @@ -402,7 +1212,7 @@ fn expr_3_1_6() -> Result<()> { #[test] fn expr_3_1_7() -> Result<()> { - let expr = "15±0.5"; + let expr = "15±0.5"; test_braille("UEB", expr, "⠼⠁⠑⠸⠖⠼⠚⠲⠑")?; return Ok(()); @@ -411,7 +1221,7 @@ fn expr_3_1_7() -> Result<()> { #[test] fn expr_3_1_8() -> Result<()> { let expr = "Area=bh= - 5·3=15"; + 5·3=15"; test_braille("UEB", expr, "⠠⠜⠑⠁⠀⠐⠶⠀⠃⠓⠀⠐⠶⠀⠼⠑⠐⠲⠼⠉⠀⠐⠶⠀⠼⠁⠑")?; return Ok(()); @@ -419,7 +1229,7 @@ fn expr_3_1_8() -> Result<()> { #[test] fn expr_3_1_9_wiris() -> Result<()> { - let expr = "3.9×4.1≃16"; + let expr = "3.9×4.1≃16"; test_braille("UEB", expr, "⠼⠉⠲⠊⠐⠦⠼⠙⠲⠁⠀⠸⠔⠀⠼⠁⠋")?; return Ok(()); @@ -427,7 +1237,7 @@ fn expr_3_1_9_wiris() -> Result<()> { #[test] fn expr_3_1_9() -> Result<()> { - let expr = "3.9×4.1≃16"; + let expr = "3.9×4.1≃16"; test_braille("UEB", expr, "⠼⠉⠲⠊⠐⠦⠼⠙⠲⠁⠀⠸⠔⠀⠼⠁⠋")?; return Ok(()); @@ -435,7 +1245,7 @@ fn expr_3_1_9() -> Result<()> { #[test] fn expr_3_1_10() -> Result<()> { - let expr = "5-3≠3-5"; + let expr = "5-3≠3-5"; test_braille("UEB", expr, "⠼⠑⠐⠤⠼⠉⠀⠐⠶⠈⠱⠀⠼⠉⠐⠤⠼⠑")?; return Ok(()); @@ -459,7 +1269,7 @@ fn ratio_3_1_12() -> Result<()> { #[test] fn alg_3_2_1_1() -> Result<()> { - let expr = "y∝x"; + let expr = "y∝x"; test_braille("UEB", expr, "⠰⠽⠀⠸⠐⠶⠀⠰⠭")?; return Ok(()); @@ -475,8 +1285,8 @@ fn alg_3_2_1_2() -> Result<()> { #[test] fn alg_3_2_2() -> Result<()> { - let expr = "0≤θ - ≤2π"; + let expr = "0≤θ + ≤2π"; test_braille("UEB", expr, "⠼⠚⠀⠸⠈⠣⠀⠨⠹⠀⠸⠈⠣⠀⠼⠃⠨⠏")?; return Ok(()); @@ -502,10 +1312,7 @@ fn alg_3_2_4() -> Result<()> { #[test] fn alg_3_2_5() -> Result<()> { let expr = "d+ab=ac"; - // BANA says use a word indicator if G1 not in first 3 cells (it is after the '='); use passage if >=2 whitespace - // This seems like a poor choice in this case since there is only one G1 indicator, but that's the BANA guidance so... - // "⠰⠰⠰⠙⠐⠖⠁⠃⠀⠐⠶⠀⠁⠉⠰⠄" - // GTM says to use the following and it is more sensisble, so I'm going with it + // GTM 1.7 / BANA 2026: one G1 symbol for standing-alone 'a' after '=' test_braille("UEB", expr, "⠙⠐⠖⠁⠃⠀⠐⠶⠀⠰⠁⠉")?; return Ok(()); @@ -541,10 +1348,10 @@ fn example_3_4_1() -> Result<()> { fn example_3_4_2() -> Result<()> { // removed some cruft from TeX output of {}^{-}2+{}^{+}3, but the basics are preserved let expr = " - − + − 2 + - − + − 3 "; test_braille("UEB", expr, "⠰⠔⠐⠤⠼⠃⠐⠖⠔⠐⠤⠼⠉")?; @@ -578,7 +1385,7 @@ fn omission_3_6_3() -> Result<()> { #[test] fn omission_3_6_4() -> Result<()> { - let expr = "3□7=10"; + let expr = "3□7=10"; test_braille("UEB", expr, "⠼⠉⠫⠼⠙⠱⠼⠛⠀⠐⠶⠀⠼⠁⠚")?; return Ok(()); @@ -637,9 +1444,21 @@ fn fraction_6_2_1() -> Result<()> { #[test] fn fraction_6_2_2() -> Result<()> { let expr = "1750 -  cm= +  cm= + 134 +  m"; + test_braille("UEB", expr, "⠼⠁⠛⠑⠚⠀⠉⠍⠀⠐⠶⠀⠼⠁⠼⠉⠌⠙⠀⠰⠍")?; + return Ok(()); + +} + +#[test] +fn fraction_6_2_2_unit() -> Result<()> { + // Same as fraction_6_2_2; unit marked via data-intent-property + let expr = "1750 +  cm= 134 -  m"; +  m"; test_braille("UEB", expr, "⠼⠁⠛⠑⠚⠀⠉⠍⠀⠐⠶⠀⠼⠁⠼⠉⠌⠙⠀⠰⠍")?; return Ok(()); @@ -648,26 +1467,30 @@ fn fraction_6_2_2() -> Result<()> { #[test] fn fraction_6_2_2_unicode_frac() -> Result<()> { let expr = "1750 -  cm= - 1¾ -  m"; +  cm= + 1¾ +  m"; test_braille("UEB", expr, "⠼⠁⠛⠑⠚⠀⠉⠍⠀⠐⠶⠀⠼⠁⠼⠉⠌⠙⠀⠰⠍")?; return Ok(()); } #[test] -fn fraction_6_3_1() -> Result<()> { - let expr = "3/8"; - test_braille("UEB", expr, "⠼⠉⠸⠌⠼⠓")?; +fn fraction_6_2_2_unicode_frac_unit() -> Result<()> { + // Same as fraction_6_2_2_unicode_frac; unit marked via data-intent-property + let expr = "1750 +  cm= + 1¾ +  m"; + test_braille("UEB", expr, "⠼⠁⠛⠑⠚⠀⠉⠍⠀⠐⠶⠀⠼⠁⠼⠉⠌⠙⠀⠰⠍")?; return Ok(()); } #[test] -fn fraction_6_4_1() -> Result<()> { - let expr = "y=x2"; - test_braille("UEB", expr, "⠰⠰⠰⠽⠀⠐⠶⠀⠷⠭⠨⠌⠼⠃⠾⠰⠄")?; +fn fraction_6_3_1() -> Result<()> { + let expr = "3/8"; + test_braille("UEB", expr, "⠼⠉⠸⠌⠼⠓")?; return Ok(()); } @@ -716,8 +1539,9 @@ fn fraction_6_4_5() -> Result<()> { #[test] fn fraction_6_4_6() -> Result<()> { let expr = "speed=distancetime"; - // GTM lists two options: "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠰⠷⠙⠊⠎⠞⠁⠝⠉⠑⠨⠌⠞⠊⠍⠑⠾" and "⠰⠰⠰⠎⠏⠑⠑⠙⠀⠐⠶⠀⠷⠙⠊⠎⠞⠁⠝⠉⠑⠨⠌⠞⠊⠍⠑⠾⠰⠄" - test_braille("UEB", expr, "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠰⠷⠙⠊⠎⠞⠁⠝⠉⠑⠨⠌⠞⠊⠍⠑⠾")?; + // GTM 2014 §6.4 listed word-indicator and passage alternatives (uncontracted words). + // GTM 1.7.5(a) (2025) prefers two grade 1 symbol indicators with contracted words. + test_braille("UEB", expr, "⠎⠏⠑⠫⠀⠐⠶⠀⠰⠷⠙⠊⠌⠨⠑⠨⠌⠐⠞⠰⠾")?; return Ok(()); } @@ -766,7 +1590,7 @@ fn msup_7_3_7() -> Result<()> { #[test] fn msup_7_3_11() -> Result<()> { let expr = "xaby=x"; - test_braille("UEB", expr, "⠰⠰⠰⠭⠔⠷⠁⠨⠌⠃⠾⠽⠀⠐⠶⠀⠭⠰⠄")?; + test_braille("UEB", expr, "⠰⠰⠭⠔⠷⠁⠨⠌⠃⠾⠽⠀⠐⠶⠀⠰⠭")?; return Ok(()); } @@ -798,7 +1622,7 @@ fn msub_7_4_3() -> Result<()> { #[test] fn msup_7_5_1() -> Result<()> { let expr = "0.0045= - 4.5×10-3 + 4.5×10-3 "; test_braille("UEB", expr, "⠼⠚⠲⠚⠚⠙⠑⠀⠐⠶⠀⠼⠙⠲⠑⠐⠦⠼⠁⠚⠔⠣⠐⠤⠼⠉⠜")?; return Ok(()); @@ -824,7 +1648,7 @@ fn msup_7_6_2() -> Result<()> { #[test] fn msubsup_7_7_1() -> Result<()> { let expr = "x12=y23"; - test_braille("UEB", expr, "⠰⠰⠰⠭⠢⠼⠁⠔⠼⠃⠀⠐⠶⠀⠽⠢⠼⠃⠔⠼⠉⠰⠄")?; + test_braille("UEB", expr, "⠭⠰⠢⠼⠁⠔⠼⠃⠀⠐⠶⠀⠽⠰⠢⠼⠃⠔⠼⠉")?; return Ok(()); } @@ -852,7 +1676,7 @@ fn pre_sup_7_8_2() -> Result<()> { +3- =5- "; - test_braille("UEB", expr, "⠰⠰⠰⠔⠐⠤⠼⠃⠐⠖⠔⠐⠤⠼⠉⠀⠐⠶⠀⠔⠐⠤⠼⠑⠰⠄")?; + test_braille("UEB", expr, "⠰⠔⠐⠤⠼⠃⠐⠖⠔⠐⠤⠼⠉⠀⠐⠶⠀⠰⠔⠐⠤⠼⠑")?; return Ok(()); } @@ -860,9 +1684,9 @@ fn pre_sup_7_8_2() -> Result<()> { #[test] fn sum_7_9_1() -> Result<()> { - let expr = "∑x=1n + let expr = "∑x=1n xi2"; - test_braille("UEB", expr, "⠰⠰⠠⠨⠎⠨⠢⠣⠭⠐⠶⠼⠁⠜⠨⠔⠝⠭⠢⠊⠔⠼⠃")?; + test_braille("UEB", expr, "⠠⠨⠎⠨⠢⠰⠣⠭⠐⠶⠼⠁⠜⠨⠔⠝⠭⠢⠊⠔⠼⠃")?; return Ok(()); } @@ -870,7 +1694,7 @@ fn sum_7_9_1() -> Result<()> { #[test] fn lim_7_9_2() -> Result<()> { // Note: modified because passage indicator is not needed (same expr when word indicator is used) - let expr = "limx→a + let expr = "limx→a f(x)=1"; test_braille("UEB", expr, "⠰⠰⠇⠊⠍⠨⠢⠣⠭⠳⠕⠁⠜⠋⠐⠣⠭⠐⠜⠀⠐⠶⠀⠼⠁")?; return Ok(()); @@ -889,7 +1713,7 @@ fn sqrt_8_1_1() -> Result<()> { fn sqrt_8_1_2() -> Result<()> { let expr = "r= x2+y2"; - test_braille("UEB", expr, "⠰⠰⠰⠗⠀⠐⠶⠀⠩⠭⠔⠼⠃⠐⠖⠽⠔⠼⠃⠬⠰⠄")?; + test_braille("UEB", expr, "⠰⠗⠀⠐⠶⠀⠰⠰⠩⠭⠔⠼⠃⠐⠖⠽⠔⠼⠃⠬")?; return Ok(()); } @@ -899,7 +1723,7 @@ fn sqrt_8_1_3() -> Result<()> { let expr = " - 783.2×6.547 + 783.2×6.547 0.4628 @@ -915,17 +1739,17 @@ fn sqrt_8_1_4() -> Result<()> { x = - −b± + −b± b 2 - −4ac + −4ac 2a "; - test_braille("UEB", expr, "⠰⠰⠰⠭⠀⠐⠶⠀⠷⠐⠤⠃⠸⠖⠩⠃⠔⠼⠃⠐⠤⠼⠙⠰⠁⠉⠬⠨⠌⠼⠃⠰⠁⠾⠰⠄")?; + test_braille("UEB", expr, "⠰⠭⠀⠐⠶⠀⠰⠰⠷⠐⠤⠃⠸⠖⠩⠃⠔⠼⠃⠐⠤⠼⠙⠰⠁⠉⠬⠨⠌⠼⠃⠰⠁⠾")?; return Ok(()); } @@ -946,7 +1770,7 @@ fn root_8_2_2() -> Result<()> { y3+ z3 3"; - test_braille("UEB", expr, "⠰⠰⠰⠟⠀⠐⠶⠀⠩⠔⠼⠉⠭⠔⠼⠉⠐⠖⠽⠔⠼⠉⠐⠖⠵⠔⠼⠉⠬⠰⠄")?; + test_braille("UEB", expr, "⠰⠟⠀⠐⠶⠀⠰⠰⠩⠔⠼⠉⠭⠔⠼⠉⠐⠖⠽⠔⠼⠉⠐⠖⠵⠔⠼⠉⠬")?; return Ok(()); } @@ -992,7 +1816,7 @@ fn spacing_9_3_1_1() -> Result<()> { #[test] fn spacing_9_3_1_2() -> Result<()> { - let expr = "3tan45°"; + let expr = "3tan45°"; test_braille("UEB", expr, "⠼⠉⠞⠁⠝⠼⠙⠑⠘⠚")?; return Ok(()); @@ -1016,7 +1840,7 @@ fn spacing_9_3_2_1() -> Result<()> { #[test] fn spacing_9_3_2_2() -> Result<()> { - let expr = "sinθ"; + let expr = "sinθ"; test_braille("UEB", expr, "⠎⠔⠨⠹")?; return Ok(()); @@ -1041,7 +1865,9 @@ fn spacing_9_3_2_4() -> Result<()> { #[test] fn spacing_9_3_2_5() -> Result<()> { let expr = "Limx2"; - test_braille("UEB", expr, "⠰⠰⠠⠇⠊⠍⠷⠭⠨⠌⠼⠃⠾")?; + // GTM 2014 9.3.2 used a word indicator (;;,lim(x./#b)). GTM 1.7.3(a): one + // symbol indicator on the fraction open; numeric mode covers the closer. + test_braille("UEB", expr, "⠠⠇⠊⠍⠰⠷⠭⠨⠌⠼⠃⠾")?; return Ok(()); } @@ -1091,8 +1917,8 @@ fn spacing_9_3_3_5() -> Result<()> { #[test] fn spacing_9_3_3_6() -> Result<()> { - let expr = "sin2β= - 2sinβcosβ"; + let expr = "sin2β= + 2sinβcosβ"; test_braille("UEB", expr, "⠎⠔⠼⠃⠨⠃⠀⠐⠶⠀⠼⠃⠎⠊⠝⠨⠃⠉⠕⠎⠨⠃")?; return Ok(()); @@ -1121,9 +1947,9 @@ fn text_9_7_1() -> Result<()> { fn stat_9_7_2() -> Result<()> { let expr = "Exp(R)= n2+1"; - // GTM uses "⠰⠰⠰⠠⠑⠭⠏⠐⠣⠠⠗⠐⠜⠀⠐⠶⠀⠷⠝⠨⠌⠼⠃⠾⠐⠖⠼⠁⠰⠄", - // but "⠠⠑⠭⠏⠐⠣⠠⠗⠐⠜⠀⠐⠶⠀⠰⠰⠷⠝⠨⠌⠼⠃⠾⠐⠖⠼⠁" is shorter and is consistent with omission_3_6_7 and fraction_6_4_6 - test_braille("UEB", expr, "⠠⠑⠭⠏⠐⠣⠠⠗⠐⠜⠀⠐⠶⠀⠰⠰⠷⠝⠨⠌⠼⠃⠾⠐⠖⠼⠁")?; + // GTM 2014 9.7 used a passage around the whole equation. GTM 1.7.3(a): one + // symbol indicator on n/2 (same as grade1_1_7_9_5). + test_braille("UEB", expr, "⠠⠑⠭⠏⠐⠣⠠⠗⠐⠜⠀⠐⠶⠀⠰⠷⠝⠨⠌⠼⠃⠾⠐⠖⠼⠁")?; return Ok(()); } @@ -1141,7 +1967,7 @@ fn set_10_1() -> Result<()> { #[test] fn set_10_3() -> Result<()> { - let expr = "3∈A∩B"; + let expr = "3∈A∩B"; test_braille("UEB", expr, "⠼⠉⠀⠘⠑⠀⠠⠁⠨⠦⠠⠃")?; return Ok(()); @@ -1149,7 +1975,7 @@ fn set_10_3() -> Result<()> { #[test] fn set_10_4() -> Result<()> { - let expr = "A∩B⊂A∪B"; + let expr = "A∩B⊂A∪B"; test_braille("UEB", expr, "⠠⠁⠨⠦⠠⠃⠀⠘⠣⠀⠠⠁⠨⠖⠠⠃")?; return Ok(()); @@ -1158,7 +1984,7 @@ fn set_10_4() -> Result<()> { fn set_10_5() -> Result<()> { let expr = "A'∪B'= (A∩B)'"; - test_braille("UEB", expr, "⠰⠰⠰⠠⠁⠶⠨⠖⠠⠃⠶⠀⠐⠶⠀⠐⠣⠠⠁⠨⠦⠠⠃⠐⠜⠶⠰⠄")?; + test_braille("UEB", expr, "⠰⠰⠠⠁⠶⠨⠖⠠⠃⠶⠀⠐⠶⠀⠐⠣⠠⠁⠨⠦⠠⠃⠐⠜⠰⠶")?; return Ok(()); } @@ -1166,11 +1992,9 @@ fn set_10_5() -> Result<()> { #[test] fn set_10_6() -> Result<()> { // Note: example uses the wrong char "├" in the display -- should be "⊢" - let expr = "[(p∨q)∧¬p] + let expr = "[(p∨q)∧¬p] ⊢q"; - // Acceptable: GTM does uses a G1 passage indicator: "⠰⠰⠰⠨⠣⠐⠣⠏⠈⠖⠟⠐⠜⠈⠦⠈⠹⠏⠨⠜⠀⠸⠒⠀⠟⠰⠄" - // However, the BANA G1 standing alone rule ("...before a single letter standing alone") applies, so start in G2 mode. - // Corrected to remove the passage indicator + // GTM / BANA 2026: grade 1 passage test_braille("UEB", expr, "⠨⠣⠐⠣⠏⠈⠖⠟⠐⠜⠈⠦⠈⠹⠏⠨⠜⠀⠸⠒⠀⠰⠟")?; return Ok(()); @@ -1187,9 +2011,7 @@ fn example_11_5_1_2() -> Result<()> { #[test] fn example_11_5_1_3() -> Result<()> { let expr = "f'(x)"; - // Acceptable: GTM uses a G1 start indicator: "⠰⠰⠋⠶⠐⠣⠭⠐⠜" - // However, BANA says don't use a word indicator if G1 is in first 3 cells (the ':' needs it) - // Corrected to avoid word indicator + // GTM / BANA 2026: grade 1 word indicator test_braille("UEB", expr, "⠋⠰⠶⠐⠣⠭⠐⠜")?; return Ok(()); @@ -1197,7 +2019,7 @@ fn example_11_5_1_3() -> Result<()> { #[test] fn example_11_5_1_4() -> Result<()> { - let expr = "∂y∂x"; + let expr = "∂y∂x"; test_braille("UEB", expr, "⠰⠰⠷⠈⠙⠽⠨⠌⠈⠙⠭⠾")?; return Ok(()); @@ -1205,11 +2027,11 @@ fn example_11_5_1_4() -> Result<()> { #[test] fn example_11_5_2() -> Result<()> { - let expr = "∫23(2x+1)dx + let expr = "∫23(2x+1)dx =x2+x23 =(32+3)-(22+2) =12-6=6"; - test_braille("UEB", expr, "⠰⠰⠰⠮⠢⠼⠃⠔⠼⠉⠐⠣⠼⠃⠭⠐⠖⠼⠁⠐⠜⠙⠭⠀⠐⠶⠀⠨⠣⠭⠔⠼⠃⠐⠖⠭⠨⠜⠢⠼⠃⠔⠼⠉⠀⠐⠶⠀⠐⠣⠼⠉⠔⠼⠃⠐⠖⠼⠉⠐⠜⠐⠤⠐⠣⠼⠃⠔⠼⠃⠐⠖⠼⠃⠐⠜⠀⠐⠶⠀⠼⠁⠃⠐⠤⠼⠋⠀⠐⠶⠀⠼⠋⠰⠄")?; + test_braille("UEB", expr, "⠰⠰⠮⠢⠼⠃⠔⠼⠉⠐⠣⠼⠃⠭⠐⠖⠼⠁⠐⠜⠙⠭⠀⠐⠶⠀⠨⠣⠭⠰⠔⠼⠃⠐⠖⠭⠨⠜⠢⠼⠃⠔⠼⠉⠀⠐⠶⠀⠐⠣⠼⠉⠔⠼⠃⠐⠖⠼⠉⠐⠜⠐⠤⠐⠣⠼⠃⠔⠼⠃⠐⠖⠼⠃⠐⠜⠀⠐⠶⠀⠼⠁⠃⠐⠤⠼⠋⠀⠐⠶⠀⠼⠋")?; return Ok(()); } @@ -1229,19 +2051,19 @@ fn example_11_5_3() -> Result<()> { = n! - r!(n−r)! + r!(n−r)! "; // modified to use "shape" as recommended in a comment on this example - test_braille("UEB", expr, "⠰⠰⠰⠔⠝⠠⠉⠢⠗⠀⠐⠶⠀⠐⠣⠝⠰⠻⠗⠐⠜⠀⠐⠶⠀⠷⠝⠖⠨⠌⠗⠖⠐⠣⠝⠐⠤⠗⠐⠜⠖⠾⠰⠄")?; + test_braille("UEB", expr, "⠰⠰⠔⠝⠠⠉⠢⠗⠀⠐⠶⠀⠐⠣⠝⠰⠻⠗⠐⠜⠀⠐⠶⠀⠰⠰⠷⠝⠖⠨⠌⠗⠖⠐⠣⠝⠐⠤⠗⠐⠜⠖⠾")?; return Ok(()); } #[test] fn example_11_5_4() -> Result<()> { - let expr = "a∗(b◦c) - =(a∗b)◦(a∗c)"; + let expr = "a∗(b◦c) + =(a∗b)◦(a∗c)"; test_braille("UEB", expr, "⠁⠐⠔⠐⠣⠃⠐⠴⠉⠐⠜⠀⠐⠶⠀⠐⠣⠁⠐⠔⠃⠐⠜⠐⠴⠐⠣⠁⠐⠔⠉⠐⠜")?; return Ok(()); @@ -1252,11 +2074,11 @@ fn example_11_5_5_2() -> Result<()> { let expr = " f - − 1 + − 1 : Y - → + → X "; test_braille("UEB", expr, "⠰⠰⠰⠋⠔⠣⠐⠤⠼⠁⠜⠒⠀⠠⠽⠀⠳⠕⠀⠠⠭⠰⠄")?; @@ -1268,17 +2090,17 @@ fn example_11_5_5_2() -> Result<()> { fn example_11_5_5_3() -> Result<()> { // this comes from MathJax let expr = " - ∀ + ∀ y - ∈ + ∈ Y - ∃ + ∃ x - ∈ + ∈ X "; - test_braille("UEB", expr, "⠰⠰⠰⠘⠁⠽⠀⠘⠑⠀⠠⠽⠀⠘⠢⠭⠀⠘⠑⠀⠠⠭⠰⠄")?; + test_braille("UEB", expr, "⠘⠁⠽⠀⠘⠑⠀⠰⠠⠽⠀⠘⠢⠭⠀⠘⠑⠀⠰⠠⠭")?; return Ok(()); } @@ -1291,7 +2113,7 @@ fn example_11_5_6() -> Result<()> { x + y = 6 return Ok(()); } "; - test_braille("UEB", expr, "⠰⠰⠰⠸⠣⠐⠣⠭⠂⠀⠽⠐⠜⠀⠸⠳⠀⠭⠐⠖⠽⠀⠐⠶⠀⠼⠋⠸⠜⠰⠄")?; + test_braille("UEB", expr, "⠸⠣⠐⠣⠰⠭⠂⠀⠰⠽⠐⠜⠀⠸⠳⠀⠭⠐⠖⠽⠀⠐⠶⠀⠼⠋⠸⠜")?; return Ok(()); } @@ -1315,7 +2137,7 @@ fn example_11_6() -> Result<()> { fn bar_over_12_1_1() -> Result<()> { let expr = "x_= 10+11+123"; - test_braille("UEB", expr, "⠰⠰⠰⠭⠱⠀⠐⠶⠀⠷⠼⠁⠚⠐⠖⠼⠁⠁⠐⠖⠼⠁⠃⠨⠌⠼⠉⠾⠰⠄")?; + test_braille("UEB", expr, "⠭⠰⠱⠀⠐⠶⠀⠰⠷⠼⠁⠚⠐⠖⠼⠁⠁⠐⠖⠼⠁⠃⠨⠌⠼⠉⠾")?; return Ok(()); } @@ -1346,8 +2168,8 @@ fn dot_12_1_4() -> Result<()> { #[test] fn dot_12_1_5() -> Result<()> { - let expr = "0.561˙ - 23˙"; + let expr = "0.561˙ + 23˙"; test_braille("UEB", expr, "⠼⠚⠲⠑⠋⠣⠼⠁⠜⠘⠲⠼⠃⠣⠼⠉⠜⠘⠲")?; return Ok(()); @@ -1355,7 +2177,7 @@ fn dot_12_1_5() -> Result<()> { #[test] fn dot_12_1_6_single() -> Result<()> { - let expr = "x˙"; + let expr = "x˙"; test_braille("UEB", expr, "⠭⠘⠲")?; return Ok(()); @@ -1363,7 +2185,7 @@ fn dot_12_1_6_single() -> Result<()> { #[test] fn dot_12_1_6_double() -> Result<()> { - let expr = "x¨"; + let expr = "x¨"; test_braille("UEB", expr, "⠰⠰⠭⠨⠔⠣⠲⠲⠜")?; return Ok(()); @@ -1372,9 +2194,7 @@ fn dot_12_1_6_double() -> Result<()> { #[test] fn hat_12_1_7() -> Result<()> { let expr = "AB^C"; - // Acceptable: GTM uses a G1 start indicator: "⠰⠰⠠⠁⠠⠃⠐⠱⠠⠉" - // BANA says use a word indicator if G1 not in first 3 cells (modified it to not count cap indicators since that helps with GTM compatibility) - // Corrected to skip the G1 indicator at the start (it's debatable as to which is better) + // GTM / BANA 2026: grade 1 word indicator test_braille("UEB", expr, "⠠⠁⠠⠃⠰⠐⠱⠠⠉")?; return Ok(()); @@ -1400,7 +2220,7 @@ fn arrow_under_12() -> Result<()> { #[test] fn bar_12_2_1() -> Result<()> { - let expr = "xy¯"; + let expr = "xy¯"; test_braille("UEB", expr, "⠰⠰⠭⠔⠣⠽⠱⠜")?; return Ok(()); @@ -1408,7 +2228,7 @@ fn bar_12_2_1() -> Result<()> { #[test] fn bar_12_2_2() -> Result<()> { - let expr = "xy¯"; + let expr = "xy¯"; test_braille("UEB", expr, "⠰⠰⠣⠭⠔⠽⠜⠱")?; return Ok(()); @@ -1416,7 +2236,7 @@ fn bar_12_2_2() -> Result<()> { #[test] fn shape_14_1_1_1() -> Result<()> { - let expr = "△ ABC"; + let expr = "△ ABC"; test_braille("UEB", expr, "⠰⠫⠼⠉⠀⠠⠠⠁⠃⠉")?; return Ok(()); @@ -1424,7 +2244,7 @@ fn shape_14_1_1_1() -> Result<()> { #[test] fn shape_14_1_2_1() -> Result<()> { - let expr = "△ABC"; + let expr = "△ABC"; test_braille("UEB", expr, "⠰⠫⠼⠉⠱⠠⠠⠁⠃⠉")?; return Ok(()); @@ -1433,11 +2253,11 @@ fn shape_14_1_2_1() -> Result<()> { #[test] fn shape_14_1_2_2() -> Result<()> { // the for the shapes are wrong -- but it isn't clear what they should be (from WIRIS editor) - let expr = "{□, -  ◍, -  ▲, + let expr = "{□, +  ◍, +  ▲, return Ok(()); -  ▧ …}"; +  ▧ …}"; test_braille("UEB", expr, "⠸⠣⠰⠫⠼⠙⠱⠂⠀⠨⠫⠿⠱⠂⠀⠸⠫⠼⠉⠱⠂⠀⠨⠫⠼⠙⠀⠲⠲⠲⠸⠜")?; return Ok(()); } @@ -1584,11 +2404,8 @@ fn omission_15_4_1() -> Result<()> { #[test] fn chem_16_2_8() -> Result<()> { let expr = "Ca(OH)2"; - // Acceptable: GTM does not use a G1 start indicator: "⠠⠉⠁⠐⠣⠠⠕⠠⠓⠐⠜⠰⠢⠼⠃" - // However, BANA says use a word indicator if G1 not in first 3 cells (it is before the subscript near the end); use passage if >=2 whitespace - // This seems like a debateable choice in this case since there is only one G1 indicator, but that's the BANA guidance so... - // Corrected to use word indicator - test_braille("UEB", expr, "⠰⠰⠠⠉⠁⠐⠣⠠⠕⠠⠓⠐⠜⠢⠼⠃")?; + // GTM / BANA 2026: single G1 symbol for subscript (no word indicator) + test_braille("UEB", expr, "⠠⠉⠁⠐⠣⠠⠕⠠⠓⠐⠜⠰⠢⠼⠃")?; return Ok(()); } @@ -1623,7 +2440,7 @@ fn chem_16_2_9() -> Result<()> { - ⋅ + ⋅ @@ -1658,11 +2475,8 @@ fn chem_16_2_9() -> Result<()> { "; - // Acceptable: GTM does not use a G1 start indicator: "⠠⠉⠥⠠⠎⠠⠕⠰⠢⠼⠙⠐⠲⠼⠑⠠⠓⠢⠼⠃⠠⠕" - // However, BANA says use a word indicator if G1 not in first 3 cells (it is before the subscript); use passage if >=2 whitespace - // This seems like a debatable choice in this case since there is only one G1 indicator, but that's the BANA guidance so... - // Corrected to use word indicator - test_braille("UEB", expr, "⠰⠰⠠⠉⠥⠠⠎⠠⠕⠢⠼⠙⠐⠲⠼⠑⠠⠓⠢⠼⠃⠠⠕")?; + // GTM / BANA 2026: single G1 symbol for first subscript (no word indicator) + test_braille("UEB", expr, "⠠⠉⠥⠠⠎⠠⠕⠰⠢⠼⠙⠐⠲⠼⠑⠠⠓⠢⠼⠃⠠⠕")?; return Ok(()); } @@ -1693,14 +2507,14 @@ fn chem_16_2_12() -> Result<()> { R - ⋅ + ⋅ CH ( OH ) - ⋅ + ⋅ CH @@ -1708,7 +2522,7 @@ fn chem_16_2_12() -> Result<()> { 2 - ⋅ + ⋅ CH @@ -1716,7 +2530,7 @@ fn chem_16_2_12() -> Result<()> { 2 - ⋅ + ⋅ CO @@ -1726,9 +2540,8 @@ fn chem_16_2_12() -> Result<()> { H "; - // GTM uses G2 mode and has two G1 indicators (in middle and near end). That definitely violates BANA guidelines and maybe there guidelines. - // I have switched it to G1 word mode, which seems better (same length, but no switching) - test_braille("UEB", expr, "⠰⠰⠠⠠⠠⠗⠐⠲⠉⠓⠐⠣⠕⠓⠐⠜⠐⠲⠉⠓⠢⠼⠃⠐⠲⠉⠓⠢⠼⠃⠐⠲⠉⠕⠢⠼⠃⠰⠓⠠⠄")?; + // GTM / BANA 2026: G2 with inline G1 symbols (not forced word mode) + test_braille("UEB", expr, "⠠⠠⠠⠗⠐⠲⠡⠐⠣⠕⠓⠐⠜⠐⠲⠡⠰⠢⠼⠃⠐⠲⠉⠓⠢⠼⠃⠐⠲⠉⠕⠢⠼⠃⠰⠓⠠⠄")?; return Ok(()); } @@ -1815,7 +2628,7 @@ fn chem_16_5_1() -> Result<()> { - → + → Na @@ -1861,7 +2674,7 @@ fn chem_16_5_1() -> Result<()> { "#; - test_braille("UEB", expr, "⠰⠰⠰⠼⠃⠠⠝⠁⠠⠕⠠⠓⠐⠖⠠⠓⠢⠼⠃⠠⠎⠠⠕⠢⠼⠙⠀⠳⠕⠀⠠⠝⠁⠢⠼⠃⠠⠎⠠⠕⠢⠼⠙⠐⠖⠼⠃⠠⠓⠢⠼⠃⠠⠕⠰⠄")?; + test_braille("UEB", expr, "⠼⠃⠠⠝⠁⠠⠕⠠⠓⠐⠖⠠⠓⠢⠼⠃⠠⠎⠠⠕⠢⠼⠙⠀⠰⠳⠕⠀⠠⠝⠁⠰⠢⠼⠃⠠⠎⠠⠕⠢⠼⠙⠐⠖⠼⠃⠠⠓⠢⠼⠃⠠⠕")?; return Ok(()); } @@ -1872,7 +2685,7 @@ fn chem_16_5_2() -> Result<()> { let expr = r#" N2 - →Haber processH2 + →Haber processH2 N H3 @@ -1952,7 +2765,7 @@ fn chem_16_5_4() -> Result<()> { - → + → @@ -1983,7 +2796,7 @@ fn chem_16_5_4() -> Result<()> { - ⋅ + ⋅ @@ -1995,7 +2808,7 @@ fn chem_16_5_4() -> Result<()> { - ⋅ + ⋅ @@ -2005,7 +2818,7 @@ fn chem_16_5_4() -> Result<()> { - → + → @@ -2040,7 +2853,7 @@ fn chem_16_5_4() -> Result<()> { - ⋅ + ⋅ @@ -2052,7 +2865,7 @@ fn chem_16_5_4() -> Result<()> { - ⋅ + ⋅ @@ -2111,16 +2924,16 @@ fn chem_16_5_5() -> Result<()> { - ↽ + ↽ - − + − - − + − - ⇀ + ⇀ @@ -2128,7 +2941,7 @@ fn chem_16_5_5() -> Result<()> { "#; - test_braille("UEB", expr, "⠰⠰⠰⠠⠏⠃⠔⠣⠐⠖⠐⠖⠜⠐⠖⠼⠃⠰⠑⠀⠘⠸⠶⠀⠠⠏⠃⠰⠄")?; + test_braille("UEB", expr, "⠰⠰⠠⠏⠃⠔⠣⠐⠖⠐⠖⠜⠐⠖⠼⠃⠰⠑⠀⠰⠘⠸⠶⠀⠠⠏⠃")?; return Ok(()); } diff --git a/tests/braille/UEB/other.rs b/tests/braille/UEB/other.rs index 7dda38f6c..9b8a1ab29 100644 --- a/tests/braille/UEB/other.rs +++ b/tests/braille/UEB/other.rs @@ -12,7 +12,7 @@ use anyhow::Result; #[test] fn overscript_grouping_aph_5_4_8() -> Result<()> { // this test was added because #220 (failed to add grouping around overscript) - let expr = " MN ↔ "; + let expr = " MN ↔ "; test_braille("UEB", expr, "⠰⠰⠣⠠⠠⠍⠝⠜⠨⠔⠳⠺⠗⠕")?; return Ok(()); @@ -39,7 +39,7 @@ fn blank_aph_7_1_ex5() -> Result<()> { #[test] fn word_symbol_aph_10_3_11() -> Result<()> { // this test was added because ⊻ (U+22bb) uses a 'G1 Word mode' char, so is different than others - let expr = " p ⊻ q "; + let expr = " p ⊻ q "; test_braille("UEB", expr, "⠰⠰⠏⠈⠖⠠⠱⠟")?; return Ok(()); @@ -247,6 +247,23 @@ fn ueb_no_italic_typeform_for_math_letters() -> Result<()> { return Ok(()); } +#[test] +fn ueb_space_because_and_qed() -> Result<()> { + // ∵ and ∎ are spaced like ∴ (GTM 11 miscellaneous; same mo AddSpaces path as 1.7.9). + test_braille("UEB", "∵x=1", "⠈⠌⠀⠰⠭⠀⠐⠶⠀⠼⠁")?; + test_braille("UEB", "x=1∎", "⠰⠭⠀⠐⠶⠀⠼⠁⠀⠸⠫⠼⠙⠱")?; + return Ok(()); +} + +#[test] +fn ueb_math_typeform_not_part_of_word() -> Result<()> { + // Double-struck/script/fraktur letters are math alphabets, not part of an English + // letters-sequence, so a following whole word still takes its contraction (time → ⠐⠞). + test_braille("UEB", "Rtime", "⠈⠆⠠⠗⠐⠞")?; + test_braille("UEB", "R", "⠈⠆⠰⠠⠗")?; + return Ok(()); +} + #[test] fn ueb_italic_typeform_for_digits() -> Result<()> { // UEB uses italic typeform for digits (GTM 2.7 emphasis of numeric material). From 9ea98c23e8c44eb19975f3da4a0284b5eaf0c056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dziwisz?= Date: Wed, 2 Sep 2026 15:42:45 +0200 Subject: [PATCH 085/120] pl: fix five speech/navigation defects and catch up with recent en changes (#738) * pl: speak up tack (U+22A5) as perpendicular, matching en Follow-up to 45aefb2d for Polish. Editors commonly emit U+22A5 where perpendicularity is meant, so both U+27C2 and U+22A5 now say "jest prostopadle do". LiteralSpeak keeps reading U+22A5 literally as "dol". Previously Polish said "dol" for U+22A5, so the same formula was read differently in Polish than in English. Adds up_tack_330 to tests/Languages/pl/alphabets.rs, mirroring the English test. * pl: adopt the `|` argument-glue syntax from definitions.yaml Follow-up to 4347d884 for Polish. That commit added the `|` syntax and updated the Polish tests, but not the Polish rule files, so several intents lost their function name in speech. Fixed, with the spoken output before -> after: quotient "podzielone przez z x przecinek, y" -> "czesc calkowita z x podzielone przez y" remainder "podzielone przez z x przecinek, y" -> "reszta z x podzielone przez y" set-difference "i z wielka a przecinek, wielka b" -> "roznica zbiorow z wielka a i wielka b" polar-coordinate "przecinek z x przecinek, y" -> "wspolrzedna biegunowa z x przecinek, y" The `|` syntax needs the function-intent rule to call IntentFunctionUseArityPath / IntentFunctionGlueBefore. Only en and hu had it, so it is now ported to pl (with "of" -> "z"). Also here: * SharedRules/geometry.yaml: the `coordinate` rule matched "." instead of "not(*[@arg])", so it swallowed coordinate($x,...) intents that should fall through to IntentMappings. Its name was also mistranslated as "przecinek" (comma) rather than "punkt" (point). * transpose: fixity order now matches en (postfix first). The function form is tested explicitly via intent='transpose:function($x)', as en does. * empty-set: added, it was missing. NOT adopted: arity templates ("| po | od,do") for sum/product. intent_function_glue_before in src/infer_intent.rs hardcodes the English word "of" for the last argument, which yields "suma po i of x" in Polish. This affects every non-English language; hu avoids it the same way. The binary separator form works correctly and is what this commit uses. * pl: fix navigation direction words, unreachable for any translation Follow-up to ec36e057 and 080ca162 for Polish, but it also fixes a long-standing Polish bug rather than only porting the new code. navigate.yaml compared the suffix of $NavCommand (always English, e.g. "ZoomIn") against a substring offset by the length of the SPOKEN $Prefix. For English those are the same; for any translation they are not. With "przybliz" (8 chars) vs "Zoom" (4), "ZoomIn" was cut to "" instead of "In", so ALL 16 branches were dead: ZoomIn -> "" (want "In") ZoomOutAll -> "ll" (want "OutAll") MoveNext -> "t" (want "Next") DescribeNext -> "ibeNext" (want "Next") Users heard "przejdz; do mianownika" with no direction, never "przejdz w prawo". Using $CommandOffset, as ec36e057 introduced, fixes all of them. Two more things here: * Polish needs two verbs where English reuses "zoom": "przybliz na zewnatrz" (zoom in outwards) is self-contradictory, so ZoomOut* now says "oddal". The direction word for plain In/Out is dropped, as the verb already carries it: "przybliz" / "oddal", and "przybliz maksymalnie" / "oddal maksymalnie". * Ports into-or-out-of-prefix-or-silent-without-parts (080ca162) and the SpeakIntentName fallbacks in into-or-out-of-default, keeping our own Polish preposition logic (including "ze stopnia" euphony). Adds zoom_speech_pl and move_char_speech_pl. Both fail on the old formula, showing the missing direction word, so they do discriminate. * pl: announce table column and row separators Follow-up to fbc49bb7 (#679) for Polish. The HasVisibleColumnLine / HasVisibleRowLine rules were missing from pl/SharedRules/default.yaml, so visible lines in a matrix were silent for Polish users, e.g. an augmented matrix was read exactly like a plain one. before: "2 na 3 macierz rozszerzona; wiersz 1; 3, 1, 4; ..." after: "2 na 3 macierz rozszerzona; wiersz 1; 3, 1, separator, 4; ..." Two existing tests (augmented_matrix_2x3, augmented_matrix_3x4_end_matrix) were pinning the pre-#679 output; their English counterparts already expect "separator", so they are updated rather than worked around. Adds dashed_augmented_matrix_separator and matrix_row_separator, ported from tests/Languages/en/mtable.rs. Removing either rule fails all four tests, so they discriminate. * pl: clear the remaining untranslated unicode entries The audit tool reported 81 unicode entries whose text equals the English source. Reviewing them one by one, only two were actually untranslated: U+2127 "mhos" -> "mho" (unit, as nb and sv have it) U+2644 "Saturn" (Polish spelling is the same; key raised) The other 75 are correct as-is and only needed the verified key: * 30 Roman numerals (U+2160..U+217F) spelled out letter by letter * 29 space, PUA and zero-width entries with no speech at all * 6 typographic ligatures (ff, fl, ffi, ffl, ft, st) * 10 proper names and symbols (spesmilos, paragraphos, hypodiastole, digamma, differential d, imaginary j, oV, pH) Four more in unicode.yaml (digit separator, space, U+2062, U+2063) likewise carry no translatable speech. Raising the key on an entry whose text legitimately matches English is what the convention is for; it is not the same as marking English text as verified. Only the wording of "mhos" changed - the diff is otherwise key case only, checked line by line. Audit's "untranslated" for unicode files: 81 -> 0. --- Rules/Languages/pl/SharedRules/default.yaml | 47 ++++- Rules/Languages/pl/SharedRules/geometry.yaml | 10 +- Rules/Languages/pl/definitions.yaml | 35 ++-- Rules/Languages/pl/navigate.yaml | 80 ++++++--- Rules/Languages/pl/unicode-full.yaml | 173 +++++++++---------- Rules/Languages/pl/unicode.yaml | 8 +- src/navigate.rs | 53 ++++++ tests/Languages/pl/alphabets.rs | 15 ++ tests/Languages/pl/definitions.rs | 40 ++++- tests/Languages/pl/mtable.rs | 47 ++++- 10 files changed, 356 insertions(+), 152 deletions(-) diff --git a/Rules/Languages/pl/SharedRules/default.yaml b/Rules/Languages/pl/SharedRules/default.yaml index ae139ebd6..531f57a5d 100644 --- a/Rules/Languages/pl/SharedRules/default.yaml +++ b/Rules/Languages/pl/SharedRules/default.yaml @@ -513,6 +513,18 @@ - x: "count(preceding-sibling::*)+IfThenElse(parent::m:mlabeledtr, 0, 1)" - pause: medium - x: "*" + - test: + if: "HasVisibleColumnLine(../.., count(preceding-sibling::*) + 1)" + then: + - pause: short + - T: "separator" + - test: + # Separator wiersza zapowiadamy tylko po ostatniej komórce wiersza, żeby + # wybrzmiał raz na każdą granicę poziomą. + if: "count(following-sibling::*) = 0 and HasVisibleRowLine(../.., count(../preceding-sibling::*) + 1)" + then: + - pause: short + - T: "separator wiersza" - 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 @@ -737,21 +749,38 @@ - name: function-intent # uncaught intent -- speak as foo of arg1 comma arg2 .... The MathML spec requires arguments to functions + # IntentMappings mogą dodać po nazwie opcje spoiwa "| ...": + # * jedna opcja bez przecinka: separator dwuargumentowy po "z" + # (np. "| podzielone przez" daje "część całkowita z x podzielone przez y") + # * wiele opcji albo opcja z przecinkami: szablony arności; k słów spoiwa + # opisuje k+1 argumentów (np. "| po | od,do") + # * gdy arność nie pasuje, argumenty rozdziela przecinek tag: "*" match: "count(*)>0" replace: - x: "SpeakIntentName(name(.), $Verbosity, 'function')" - test: - if: "$Verbosity != 'Terse' and not(contains(@data-intent-property, ':literal:')) and - not(count(*)=2 and (IsInDefinition(*[1], 'TrigFunctionNames') or IsInDefinition(name(.), 'TerseFunctionNames')) and IsNode(*[2], 'simple'))" - then: [T: "z", pause: auto] # phrase(sine 'of' 5) - - insert: - nodes: "*" - replace: - - test: - if: "not(contains(@data-intent-property, ':literal:'))" - then: [x: "','"] + - 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: "z", pause: auto] # phrase(sine 'of' 5) + - 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')] )" diff --git a/Rules/Languages/pl/SharedRules/geometry.yaml b/Rules/Languages/pl/SharedRules/geometry.yaml index 37155f79c..f110a71ed 100644 --- a/Rules/Languages/pl/SharedRules/geometry.yaml +++ b/Rules/Languages/pl/SharedRules/geometry.yaml @@ -57,12 +57,16 @@ - name: coordinate tag: coordinate - match: "." + # Intencje postaci coordinate($x,...) mają @arg i spadają do function-intent / IntentMappings + match: "not(*[@arg])" replace: - - T: "przecinek" # phrase(the 'point' at 1, 2) - test: if: "$Verbosity='Verbose'" - then: [t: "w"] # phrase('the' point at 1, 2) + then: [OT: "ten"] # phrase('the' point at 1, 2) + - T: "punkt" # phrase(the 'point' at 1, 2) + - test: + if: "$Verbosity='Verbose'" + then: [T: "w"] # phrase('the' point at 1, 2) - pause: short - insert: nodes: "*" diff --git a/Rules/Languages/pl/definitions.yaml b/Rules/Languages/pl/definitions.yaml index 6dda0ebe0..9c4c6e69c 100644 --- a/Rules/Languages/pl/definitions.yaml +++ b/Rules/Languages/pl/definitions.yaml @@ -13,9 +13,9 @@ "image":"function=obraz", "mixed-fraction":"infix=i", - "quotient":"function=część całkowita; podzielone przez", + "quotient":"function=część całkowita | podzielone przez", "evaluated-at":"infix=obliczone w", - "remainder":"function=reszta; podzielone przez", + "remainder":"function=reszta | podzielone przez", "max":"function=maksimum", "min":"function=minimum", @@ -32,10 +32,10 @@ "real-part": "function=część rzeczywista", "imaginary-part": "function=część urojona: część urojona: część urojona", - "polar-coordinate":"function=współrzędna biegunowa; przecinek", - "spherical-coordinate":"function=współrzędna sferyczna; przecinek; przecinek", - "cartesian-coordinate":"function=współrzędna kartezjańska; przecinek", - "coordinate":"function=współrzędna; przecinek", + "polar-coordinate":"function=współrzędna biegunowa", + "spherical-coordinate":"function=współrzędna sferyczna", + "cartesian-coordinate":"function=współrzędna kartezjańska", + "coordinate":"function=współrzędna", "floor":"function=podłoga", "ceiling":"function=sufit", "round":"function=zaokrąglenie", @@ -51,16 +51,23 @@ ### Sets "set": "function=; zbiór: zbiór", - "set-difference":"function=różnica zbiorów; i || infix=minus", # NOTE: not tested + "set-difference":"function=różnica zbiorów | i || infix=minus", "complement":"function=dopełnienie", + "empty-set":"nofix=zbiór pusty", "cardinality":"function=moc zbioru", "list":"function=lista", "tuple": "function=; krotka: krotka", ### Sequence and Series - "sum":"function=; suma po: suma; ", - "product":"function=iloczyn || function=iloczyn po || function=iloczyn od; do", + # UWAGA: NIE używamy tu szablonów arności ("| po | od,do"), choć angielski je ma. + # Powód (zmierzone 31.08.2026): intent_function_glue_before w src/infer_intent.rs + # zwraca dla ostatniego argumentu ZASZYTE W KODZIE angielskie "of", więc polskie + # wyjście brzmiałoby "suma po i of x". To defekt rdzenia dotyczący każdego języka + # poza angielskim; węgierski uniknął go tak samo, zostawiając prostą postać. + # Separator binarny (jedna opcja "| słowo") działa poprawnie i jest używany niżej. + "sum":"function=suma", + "product":"function=iloczyn", ### Elementary classical functions @@ -104,7 +111,7 @@ "median":"function=mediana", "mode":"function=dominanta", - "conditional-probability":"function=prawdopodobieństwo; pod warunkiem", # NOTE: Check test + "conditional-probability":"function=prawdopodobieństwo | pod warunkiem", ### Linear Algebra @@ -120,7 +127,7 @@ "unit-vector":"prefix=wektor jednostkowy", "identity-matrix":"nofix=macierz jednostkowa", # NOTE: no function specified - "transpose":"function=transpozycja || postfix=transponowane", + "transpose":"postfix=transponowane || function=transpozycja", "dimensional-product":"infix=na", # INFIX @@ -150,13 +157,13 @@ ### General Concepts "fenced-group":"function=grupa w nawiasach", - "ordered-pair": "function=; para; i", + "ordered-pair": "function=para | i", "indexed-by": "infix=; indeks dolny; koniec indeksu dolnego", "highlight":"postfix=podświetlone", "least-common-denominator":"function=najmniejszy wspólny mianownik", "rate":"infix=na", - "translation":"function=przesunięcie o; przecinek", + "translation":"function=przesunięcie o", "constraint":"infix=; przy warunku; ", "binomial-coefficient":"infix=po", @@ -264,7 +271,7 @@ ## Default fixity nofix "diameter":"nofix=d: średnica", "distance":"nofix=d; D: odległość", - "probability":"nofix=P: prawdopodobieństwo", + "probability":"nofix=P: prawdopodobieństwo || function=prawdopodobieństwo", "radius":"nofix=r: promień", "volume":"nofix=V: objętość || function=objętość", "exponential-e":"nofix=e", diff --git a/Rules/Languages/pl/navigate.yaml b/Rules/Languages/pl/navigate.yaml index cfdecbcb4..c99b60490 100644 --- a/Rules/Languages/pl/navigate.yaml +++ b/Rules/Languages/pl/navigate.yaml @@ -53,39 +53,46 @@ then: [t: "maksymalnie oddalono", pause: "medium"] else: - test: - - if: "starts-with($NavCommand, 'Zoom')" - then: [set_variables: [Prefix: "'przybliż'"]] # phrase('zoom' in to see more details) + # CommandOffset to 1 + długość ANGIELSKIEGO rdzenia $NavCommand + # (Zoom/Move/Read/Describe), a NIE długość wymawianego $Prefix - + # dzięki temu tłumaczenie może mieć słowo Prefix innej długości. + # Angielskie "zoom" obsługuje oba kierunki, polski ma dwa różne + # czasowniki - "przybliż na zewnątrz" byłoby sprzeczne. + - if: "starts-with($NavCommand, 'ZoomOut')" + then: [set_variables: [Prefix: "'oddal'", CommandOffset: "5"]] # phrase('zoom' out to see less detail) + - else_if: "starts-with($NavCommand, 'Zoom')" + then: [set_variables: [Prefix: "'przybliż'", CommandOffset: "5"]] # phrase('zoom' in to see more details) - else_if: "starts-with($NavCommand, 'Move')" - then: [set_variables: [Prefix: "'przejdź'"]] # phrase('move' to next entry in table) + then: [set_variables: [Prefix: "'przejdź'", CommandOffset: "5"]] # phrase('move' to next entry in table) - else_if: "starts-with($NavCommand, 'Read')" - then: [set_variables: [Prefix: "'odczytaj'"]] # phrase('read' to next entry in table) + then: [set_variables: [Prefix: "'odczytaj'", CommandOffset: "5"]] # phrase('read' to next entry in table) - else_if: "starts-with($NavCommand, 'Describe')" - then: [set_variables: [Prefix: "'opisz'"]] # phrase('describe' to next entry in table) + then: [set_variables: [Prefix: "'opisz'", CommandOffset: "9"]] # phrase('describe' to next entry in table) - test: if: "$Prefix != ''" then: - x: "$Prefix" - test: - - if: "substring($NavCommand, string-length($Prefix)+1) = 'In'" - then: [T: "do"] # phrase(zoom 'in' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'InAll'" + - if: "substring($NavCommand, $CommandOffset) = 'In'" + then: [] # sam czasownik "przybliż" wystarcza + - 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: "maksymalnie do środka"] # phrase(zoom 'out all the way' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Out'" - then: [t: "na zewnątrz"] # phrase(zoom 'out' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'OutAll'" + then: [T: "maksymalnie"] # phrase(zoom 'in all of the way' to see more details) + - else_if: "substring($NavCommand, $CommandOffset) = 'Out'" + then: [] # sam czasownik "oddal" wystarcza + - 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: "maksymalnie na zewnątrz"] # phrase(zoom 'out all the way' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Next'" - then: [t: "w prawo"] # phrase(move to the 'right') - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Previous'" - then: [t: "w lewo"] # phrase(move to the 'left') - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Current'" - then: [t: "bieżący"] # phrase(who is the 'current' president) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'LineStart'" - then: [t: "do początku linii"] # phrase(move 'to start of line') - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'LineEnd'" - then: [t: "do końca linii"] # phrase(move 'to end of line') + then: [T: "maksymalnie"] # phrase(zoom 'out all of the way' to see more details) + - else_if: "substring($NavCommand, $CommandOffset) = 'Next'" + then: [T: "w prawo"] # phrase(move to the 'right') + - else_if: "substring($NavCommand, $CommandOffset) = 'Previous'" + then: [T: "w lewo"] # phrase(move to the 'left') + - else_if: "substring($NavCommand, $CommandOffset) = 'Current'" + then: [T: "bieżący"] # phrase(who is the 'current' president) + - else_if: "substring($NavCommand, $CommandOffset) = 'LineStart'" + then: [T: "do początku linii"] # phrase(move 'to start of line') + - else_if: "substring($NavCommand, $CommandOffset) = 'LineEnd'" + then: [T: "do końca linii"] # phrase(move 'to end of line') - pause: "medium" - set_variables: [MatchCounter: "$MatchCounter + 1"] @@ -96,6 +103,15 @@ (name(.)='mrow' or name(.) = 'mtr' or name(.) = 'mlabeledtr' or @data-from-mathml = 'mtable')" replace: [] +- name: into-or-out-of-prefix-or-silent-without-parts + tag: "*" + # Intencje prefiksowe i ciche bez wpisu w NavigationParts (np. minus unarny, + # x z daszkiem) NIE powinny zapowiadać "do"/"na zewnątrz". + 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'" @@ -168,6 +184,24 @@ - test: - if: "$PartName != ''" then: [x: "$PartName"] + - else_if: "count(*) = 1" + then_test: + if: "$NavVerbosity = 'Verbose'" + then: # IntentMappings przez SpeakIntentName; w razie braku - nazwa intentu ze spacjami + - 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: "części" # phrase(the 'part' of the expression) - x: "count($Child2D/preceding-sibling::*) + 1" diff --git a/Rules/Languages/pl/unicode-full.yaml b/Rules/Languages/pl/unicode-full.yaml index 0fbeda954..8246f4650 100644 --- a/Rules/Languages/pl/unicode-full.yaml +++ b/Rules/Languages/pl/unicode-full.yaml @@ -291,7 +291,7 @@ - "†": [t: "obelisk"] # 0x2020 - "‡": [t: "podwójny obelisk"] # 0x2021 - - " - ": [t: " "] # 0x2000 - 0x2007 + - " - ": [T: " "] # 0x2000 - 0x2007 - "•": # 0x2022 - test: @@ -344,7 +344,7 @@ - "⁑": [t: "dwie pionowe gwiazdki"] # 0x2051 - "⁒": [t: "handlowy znak minus"] # 0x2052 - "⁗": [t: "poczwórny prim"] # 0x2057 - - "⁠": [t: ""] # 0x2060 + - "⁠": [T: ""] # 0x2060 - "⁰": [t: "do zerowej potęgi"] # 0x2070 - "ⁱ": [t: "do potęgi i"] # 0x2071 - "⁴": [t: "do czwartej potęgi"] # 0x2074 @@ -409,7 +409,7 @@ - "₴": [t: "hrywny"] # 0x20b4 - "₵": [t: "cedi"] # 0x20b5 - "₶": [t: "liwr turoński"] # 0x20b6 - - "₷": [t: "spesmilos"] # 0x20b7 + - "₷": [T: "spesmilos"] # 0x20b7 - "₸": [t: "tenge"] # 0x20b8 - "₹": [t: "rupie indyjskie"] # 0x20b9 - "₺": [t: "liry tureckie"] # 0x20ba @@ -473,7 +473,7 @@ - "№": [t: "numer"] # 0x2116 - "℥": [t: "uncje"] # 0x2125 - "Ω": [t: "omy"] # 0x2126 - - "℧": [t: "mhos"] # 0x2127 + - "℧": [T: "mho"] # 0x2127 - "℩": [t: "odwrócony jota"] # 0x2129 - "K": [t: "kelwin"] # 0x212a - "Å": [t: "angstremy"] # 0x212b @@ -522,37 +522,37 @@ - "⅞": [t: "siedem ósmych"] # 0x215e - "⅟": [t: "jeden przez"] # 0x215f - "Ⅰ": [t: "I"] # 0x2160 - - "Ⅱ": [t: "I I"] # 0x2161 - - "Ⅲ": [t: "I I I"] # 0x2162 - - "Ⅳ": [t: "I V"] # 0x2163 - - "Ⅴ": [t: "V"] # 0x2164 - - "Ⅵ": [t: "V I"] # 0x2165 - - "Ⅶ": [t: "V I I"] # 0x2166 - - "Ⅷ": [t: "V I I I"] # 0x2167 - - "Ⅸ": [t: "I X"] # 0x2168 - - "Ⅹ": [t: "X"] # 0x2169 - - "Ⅺ": [t: "X I"] # 0x216a - - "Ⅻ": [t: "X I I"] # 0x216b - - "Ⅼ": [t: "L"] # 0x216c - - "Ⅽ": [t: "C"] # 0x216d - - "Ⅾ": [t: "D"] # 0x216e - - "Ⅿ": [t: "M"] # 0x216f + - "Ⅱ": [T: "I I"] # 0x2161 + - "Ⅲ": [T: "I I I"] # 0x2162 + - "Ⅳ": [T: "I V"] # 0x2163 + - "Ⅴ": [T: "V"] # 0x2164 + - "Ⅵ": [T: "V I"] # 0x2165 + - "Ⅶ": [T: "V I I"] # 0x2166 + - "Ⅷ": [T: "V I I I"] # 0x2167 + - "Ⅸ": [T: "I X"] # 0x2168 + - "Ⅹ": [T: "X"] # 0x2169 + - "Ⅺ": [T: "X I"] # 0x216a + - "Ⅻ": [T: "X I I"] # 0x216b + - "Ⅼ": [T: "L"] # 0x216c + - "Ⅽ": [T: "C"] # 0x216d + - "Ⅾ": [T: "D"] # 0x216e + - "Ⅿ": [T: "M"] # 0x216f - "ⅰ": [t: "I"] # 0x2170 - - "ⅱ": [t: "I I"] # 0x2171 - - "ⅲ": [t: "I I I"] # 0x2172 - - "ⅳ": [t: "I V"] # 0x2173 - - "ⅴ": [t: "V"] # 0x2174 - - "ⅵ": [t: "V I"] # 0x2175 - - "ⅶ": [t: "V I I"] # 0x2176 - - "ⅷ": [t: "V I I I"] # 0x2177 - - "ⅸ": [t: "I X"] # 0x2178 - - "ⅹ": [t: "X"] # 0x2179 - - "ⅺ": [t: "X I"] # 0x217a - - "ⅻ": [t: "X I I"] # 0x217b - - "ⅼ": [t: "L"] # 0x217c - - "ⅽ": [t: "C"] # 0x217d - - "ⅾ": [t: "D"] # 0x217e - - "ⅿ": [t: "M"] # 0x217f + - "ⅱ": [T: "I I"] # 0x2171 + - "ⅲ": [T: "I I I"] # 0x2172 + - "ⅳ": [T: "I V"] # 0x2173 + - "ⅴ": [T: "V"] # 0x2174 + - "ⅵ": [T: "V I"] # 0x2175 + - "ⅶ": [T: "V I I"] # 0x2176 + - "ⅷ": [T: "V I I I"] # 0x2177 + - "ⅸ": [T: "I X"] # 0x2178 + - "ⅹ": [T: "X"] # 0x2179 + - "ⅺ": [T: "X I"] # 0x217a + - "ⅻ": [T: "X I I"] # 0x217b + - "ⅼ": [T: "L"] # 0x217c + - "ⅽ": [T: "C"] # 0x217d + - "ⅾ": [T: "D"] # 0x217e + - "ⅿ": [T: "M"] # 0x217f - "↉": [t: "zero trzecich"] # 0x2189 - "↔": [t: "lewy strzałka w prawo"] # 0x2194 - "↕": [t: "górny strzałka w dół"] # 0x2195 @@ -1081,11 +1081,6 @@ - "⊢": [t: "dowodzi"] # 0x22a2 - "⊣": [t: "jest dowodzone przez"] # 0x22a3 - "⊤": [t: "góra"] # 0x22a4 - - "⊥": # 0x22a5 - - test: - if: "$Verbosity!='Terse'" - then: [T: "jest"] - - t: "dół" - "⊦": [t: "redukuje się do"] # 0x22a6 - "⊧": [t: "modeluje"] # 0x22a7 - "⊨": # 0x22a8 @@ -1538,7 +1533,7 @@ - "♁": [t: "Ziemia"] # 0x2641 - "♂": [t: "męskie"] # 0x2642 - "♃": [t: "Jowisz"] # 0x2643 - - "♄": [t: "Saturn"] # 0x2644 + - "♄": [T: "Saturn"] # 0x2644 - "♅": [t: "Uran"] # 0x2645 - "♆": [t: "Neptun"] # 0x2646 - "♇": [t: "Pluton"] # 0x2647 @@ -1662,11 +1657,15 @@ - "➾": [t: "otwarta konturowa strzałka w prawo"] # 0x27be - "⟀": [t: "trójwymiarowy kąt"] # 0x27c0 - "⟁": [t: "biały trójkąt zawierający mały biały trójkąt"] # 0x27c1 - - "⟂": # 0x27c2 - - test: - if: "$Verbosity!='Terse'" - then: [T: "jest"] - - t: "prostopadłe do" + - "⟂⊥": # 0x27c2, 22A5 (22A5 wrongly used for perpendicular by most editors) + - test: + - if: "$SpeechStyle = 'LiteralSpeak' and text()='⊥'" # 22A5 (up tack/bottom) + then: [T: "dół"] + - else: + - test: + if: "$Verbosity!='Terse'" + then: [T: "jest"] + - T: "prostopadłe do" - "⟃": # 0x27c3 - test: if: "$Verbosity!='Terse'" @@ -2354,10 +2353,10 @@ - "⸌": [t: "lewy podniesiony pominięcia nawias kwadratowy"] # 0x2e0c - "⸍": [t: "prawy podniesiony pominięcia nawias kwadratowy"] # 0x2e0d - "⸎": [t: "redakcyjny koronis"] # 0x2e0e - - "⸏": [t: "paragraphos"] # 0x2e0f + - "⸏": [T: "paragraphos"] # 0x2e0f - "⸐": [t: "rozwidlony paragrafos"] # 0x2e10 - "⸑": [t: "odwrócony rozwidlony paragrafos"] # 0x2e11 - - "⸒": [t: "hypodiastole"] # 0x2e12 + - "⸒": [T: "hypodiastole"] # 0x2e12 - "⸓": [t: "kropkowany obelos"] # 0x2e13 - "⸔": [t: "dolny ancora"] # 0x2e14 - "⸕": [t: "górny ancora"] # 0x2e15 @@ -2466,7 +2465,7 @@ - "㍲": [t: "daltony"] # 0x3372 - "㍳": [t: "jednostki astronomiczne"] # 0x3373 - "㍴": [t: "bary"] # 0x3374 - - "㍵": [t: "o V"] # 0x3375 + - "㍵": [T: "o V"] # 0x3375 - "㍶": [t: "parseki"] # 0x3376 - "㍷": [t: "decymetry"] # 0x3377 - "㍸": [t: "decymetry do kwadratu"] # 0x3378 @@ -2559,7 +2558,7 @@ - "㏔": [t: "milibarny"] # 0x33d4 - "㏕": [t: "mile"] # 0x33d5 - "㏖": [t: "mole"] # 0x33d6 - - "㏗": [t: "p h"] # 0x33d7 + - "㏗": [T: "p h"] # 0x33d7 - "㏘": [t: "pikometry"] # 0x33d8 - "㏙": [t: "części na milion"] # 0x33d9 - "㏚": [t: "petarentgeny"] # 0x33da @@ -2992,30 +2991,30 @@ - "": [t: "stała Plancka przez dwa pi kreska"] # 0xed00 - "": [t: "lustrzane g"] # 0xed01 - "": [t: "j bez kropki"] # 0xed02 - - "": [t: "digamma"] # 0xed03 - - "ϝ": [t: "digamma"] # 0x3dd - - "": [t: "d"] # 0xed10 - - "ⅆ": [t: "d"] # 0x2146 + - "": [T: "digamma"] # 0xed03 + - "ϝ": [T: "digamma"] # 0x3dd + - "": [T: "d"] # 0xed10 + - "ⅆ": [T: "d"] # 0x2146 - "": [t: "e"] # 0xed11 - "ⅇ": [t: "e"] # 0x2147 - "": [t: "i"] # 0xed12 - "ⅈ": [t: "i"] # 0x2148 - - "": [t: "j"] # 0xed13 + - "": [T: "j"] # 0xed13 - "ⅅ": - spell: "translate('.', 'ⅅ', 'DD')" # 0xed16, 0x2145 # The private use chars are from MathType - "": [t: "całka krzywoliniowa z pętlą przeciwnie do ruchu wskazówek zegara"] # 0xee00 - "": [t: "całka krzywoliniowa z pętlą zgodnie z ruchem wskazówek zegara"] # 0xee01 - - "": [t: ""] # 0xee04 - - "": [t: ""] # 0xee05 - - "": [t: ""] # 0xee06 - - "": [t: ""] # 0xee07 - - "": [t: ""] # 0xee08 - - "": [t: ""] # 0xee09 - - "": [t: ""] # 0xee0a - - "": [t: ""] # 0xee0b - - "": [t: ""] # 0xee0c + - "": [T: ""] # 0xee04 + - "": [T: ""] # 0xee05 + - "": [T: ""] # 0xee06 + - "": [T: ""] # 0xee07 + - "": [T: ""] # 0xee08 + - "": [T: ""] # 0xee09 + - "": [T: ""] # 0xee0a + - "": [T: ""] # 0xee0b + - "": [T: ""] # 0xee0c - "": [t: "ozdobnik statusu połączenia"] # 0xee0d - "": [t: "lewy ozdobnik statusu połączenia"] # 0xee0e - "": [t: "prawy ozdobnik statusu połączenia"] # 0xee0f @@ -3040,24 +3039,24 @@ - "": [t: "kreska akcent z zamkniętym okręgiem po prawej"] # 0xee23 - "": [t: "duży kropka nad"] # 0xee24 - "": [t: "znak wyrównania"] # 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: ""] # 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: "brakujący składnik"] # 0xef41 - "": [t: "całka krzywoliniowa zgodna z ruchem wskazówek zegara ze strzałką po lewej"] # 0xef80 - "": [t: "całka z kwadrat"] # 0xef81 @@ -3564,13 +3563,13 @@ - "": [t: "prawy nawias klamrowy środkowy"] # 0xf8fd - "": [t: "prawy nawias klamrowy dolny"] # 0xf8fe - "": [t: "logo jabłka"] # 0xf8ff - - "ff": [t: "ff"] # 0xfb00 + - "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 + - "fl": [T: "fl"] # 0xfb02 + - "ffi": [T: "ffi"] # 0xfb03 + - "ffl": [T: "ffl"] # 0xfb04 + - "ſt": [T: "ft"] # 0xfb05 + - "st": [T: "st"] # 0xfb06 - "﬩": [t: "hebrajska litera alternatywny znak plus"] # 0xfb29 - "︠": [t: "lewa połowa ligatury, ozdobnik"] # 0xfe20 - "︡": [t: "prawa połowa ligatury, ozdobnik"] # 0xfe21 diff --git a/Rules/Languages/pl/unicode.yaml b/Rules/Languages/pl/unicode.yaml index 6d90fdcd3..bd3ee353a 100644 --- a/Rules/Languages/pl/unicode.yaml +++ b/Rules/Languages/pl/unicode.yaml @@ -60,9 +60,9 @@ # note: processing of ranges converts '.' into the character, so it needs to be in quotes below replace: [spell: "translate('.', 'BCDEFGHIJKLMNOPQRSTUVWXYZ', 'bcdefghijklmnopqrstuvwxyz')"] - - "0-9": [t: "."] + - "0-9": [T: "."] - - " ": [t: " "] # 0x20 + - " ": [T: " "] # 0x20 - "!": # 0x21 - test: @@ -337,8 +337,8 @@ (@data-changed='added' and ancestor-or-self::*[contains(@data-intent-property, ':literal:')]) )" then: [T: "z"] - - "⁢": [t: ""] # 0x2062 - - "⁣": [t: ""] # 0x2063 + - "⁢": [T: ""] # 0x2062 + - "⁣": [T: ""] # 0x2063 - "⁤": [T: "i"] # 0x2064 - "′": [t: "prim"] # 0x2032 - "″": [t: "podwójny prim"] # 0x2033 diff --git a/src/navigate.rs b/src/navigate.rs index a3365ef56..1fac4e766 100644 --- a/src/navigate.rs +++ b/src/navigate.rs @@ -1765,6 +1765,59 @@ mod tests { }); } + #[test] + fn zoom_speech_pl() -> Result<()> { + let mathml_str = " + b2 + d + "; + init_prefs(mathml_str, "Enhanced", "pl")?; + return MATHML_INSTANCE.with(|package_instance| { + let package_instance = package_instance.borrow(); + let mathml = get_element(&package_instance); + let speech = test_command("ZoomIn", mathml, "msup")?; + assert_eq!("przybliż; do licznika; b do kwadratu", speech); + let speech = test_command("ZoomIn", mathml, "base")?; + assert_eq!("przybliż; do podstawy; b", speech); + let speech = test_command("ZoomOut", mathml, "msup")?; + assert_eq!("oddal; z podstawy; b do kwadratu", speech); + let speech = test_command("ZoomInAll", mathml, "base")?; + assert_eq!("przybliż maksymalnie; do podstawy; b", speech); + let speech = test_command("ZoomOutAll", mathml, "mfrac")?; + assert_eq!("oddal maksymalnie; z podstawy; z licznika; ułamek, b do kwadratu, przez d, koniec ułamka", speech); + return Ok( () ); + }); + } + + #[test] + fn move_char_speech_pl() -> Result<()> { + let mathml_str = " + + + x + y + + ⁢ + z + + "; + init_prefs(mathml_str, "Character", "pl")?; + return MATHML_INSTANCE.with(|package_instance| { + let package_instance = package_instance.borrow(); + let mathml = get_element(&package_instance); + test_command("ZoomInAll", mathml, "id-3")?; + let speech = test_command("MoveNext", mathml, "id-4")?; + assert_eq!("przejdź w prawo; do mianownika; y", speech); + let speech = test_command("MoveNext", mathml, "id-7")?; + assert_eq!("przejdź w prawo; z mianownika; do pierwiastka; z", speech); + let speech = test_command("MovePrevious", mathml, "id-4")?; + assert_eq!("przejdź w lewo; z pierwiastka; do mianownika; y", speech); + let speech = test_command("MovePrevious", mathml, "id-3")?; + assert_eq!("przejdź w lewo; do licznika; x", speech); + return Ok( () ); + }); + } + #[test] fn move_char_speech_ru() -> Result<()> { let mathml_str = " diff --git a/tests/Languages/pl/alphabets.rs b/tests/Languages/pl/alphabets.rs index 5c2aef97b..eda0a1ae4 100644 --- a/tests/Languages/pl/alphabets.rs +++ b/tests/Languages/pl/alphabets.rs @@ -391,6 +391,21 @@ fn turned() -> Result<()> { } +#[test] +fn up_tack_330() -> Result<()> { + // Znak prostopadłości i znak "dół" wyglądają identycznie, a edytory + // wstawiają U+22A5 tam, gdzie chodzi o prostopadłość. Dlatego oba znaki + // mówią "prostopadłe do", a osobna reguła LiteralSpeak zachowuje dosłowne + // czytanie U+22A5 jako "dół". + let perp = "a⟂b"; // 0x27c2 + test("pl", "SimpleSpeak", perp, "a jest prostopadłe do b")?; + test("pl", "LiteralSpeak", perp, "a jest prostopadłe do b")?; + let up_tack = "a⊥b"; // 0x22a5 + test("pl", "ClearSpeak", up_tack, "a jest prostopadłe do b")?; + test("pl", "LiteralSpeak", up_tack, "a dół b")?; + return Ok(()); + } + #[test] fn unicode_typo_regressions() -> Result<()> { test("pl", "SimpleSpeak", "ⁱ", "do potęgi i")?; diff --git a/tests/Languages/pl/definitions.rs b/tests/Languages/pl/definitions.rs index 312075f80..338f7ff95 100644 --- a/tests/Languages/pl/definitions.rs +++ b/tests/Languages/pl/definitions.rs @@ -91,7 +91,14 @@ fn set_difference_basic() -> Result<()> { "#; - test("pl", "ClearSpeak", expr, "i z wielka a przecinek, wielka b")?; + // Separator dwuargumentowy: nazwa funkcji + "z" + argumenty spięte słowem "i". + // Wcześniej nazwa przepadała i zostawało samo spoiwo ("i z wielka a..."). + test( + "pl", + "ClearSpeak", + expr, + "różnica zbiorów z wielka a i wielka b", + )?; Ok(()) } @@ -107,7 +114,9 @@ fn postfix_test() -> Result<()> { T "#, - "transpozycja z x", + // Test postfiksowy: bez jawnej fixity silnik bierze PIERWSZĄ z listy + // w IntentMappings, a tam (jak w EN) postfix jest pierwszy. + "x transponowane", ), ( "highlight", @@ -205,9 +214,11 @@ fn functions_and_inverses_tests() -> Result<()> { //("fraction", "fraction x over y end fraction"), ("mixed-fraction", "x i y"), - ("quotient", "podzielone przez z x przecinek, y"), + // Separator dwuargumentowy z IntentMappings ("| podzielone przez"): + // nazwa funkcji wraca na swoje miejsce, a spoiwo łączy oba argumenty. + ("quotient", "część całkowita z x podzielone przez y"), ("evaluated-at", "x obliczone w y"), - ("remainder", "podzielone przez z x przecinek, y"), + ("remainder", "reszta z x podzielone przez y"), ("max", "maksimum z x przecinek, y przecinek, z"), ("min", "minimum z x przecinek, y przecinek, z"), @@ -228,10 +239,18 @@ fn functions_and_inverses_tests() -> Result<()> { ("real-part", "część rzeczywista"), ("imaginary-part", "część urojona"), - ("polar-coordinate", "przecinek z x przecinek, y"), - ("spherical-coordinate", "przecinek z x przecinek, y przecinek, z"), - ("cartesian-coordinate", "przecinek z x przecinek, y przecinek, z"), - ("coordinate", "przecinek, x przecinek y przecinek z"), + // Współrzędne: po usunięciu zbędnego "; przecinek" z IntentMappings + // nazwa wraca na swoje miejsce (wcześniej wyjście brzmiało "przecinek z x..."). + ("polar-coordinate", "współrzędna biegunowa z x przecinek, y"), + ( + "spherical-coordinate", + "współrzędna sferyczna z x przecinek, y przecinek, z", + ), + ( + "cartesian-coordinate", + "współrzędna kartezjańska z x przecinek, y przecinek, z", + ), + ("coordinate", "współrzędna z x przecinek, y przecinek, z"), ("floor", "podłoga z x"), ("ceiling", "sufit z x"), @@ -598,6 +617,11 @@ fn linear_algebra_tests() -> Result<()> { " .to_string() } + "transpose" => " + + x + + ".to_string(), _ => format!( " diff --git a/tests/Languages/pl/mtable.rs b/tests/Languages/pl/mtable.rs index 8403eeae2..093cb1084 100644 --- a/tests/Languages/pl/mtable.rs +++ b/tests/Languages/pl/mtable.rs @@ -265,8 +265,8 @@ fn augmented_matrix_2x3() -> Result<()> { ] "; - test("pl", "ClearSpeak", expr, "2 na 3 macierz rozszerzona; wiersz 1; 3, 1, 4; wiersz 2; 0, 2, 6")?; - test("pl", "SimpleSpeak", expr, "2 na 3 macierz rozszerzona; wiersz 1; 3, 1, 4; wiersz 2; 0, 2, 6")?; + test("pl", "ClearSpeak", expr, "2 na 3 macierz rozszerzona; wiersz 1; 3, 1, separator, 4; wiersz 2; 0, 2, separator, 6")?; + test("pl", "SimpleSpeak", expr, "2 na 3 macierz rozszerzona; wiersz 1; 3, 1, separator, 4; wiersz 2; 0, 2, separator, 6")?; Ok(()) } @@ -897,9 +897,9 @@ let expr = " "; test_ClearSpeak("pl", "ClearSpeak_Matrix", "EndMatrix", - expr, "3 na 4 macierz rozszerzona; wiersz 1; kolumna 1; 1, kolumna 2; 2, kolumna 3; minus 1, kolumna 4; 3; wiersz 2; kolumna 1; minus 3, kolumna 2; 3, kolumna 3; minus 1, kolumna 4; 2; wiersz 3; kolumna 1; 2, kolumna 2; 3, kolumna 3; 2, kolumna 4; minus 1; koniec macierzy")?; + expr, "3 na 4 macierz rozszerzona; wiersz 1; kolumna 1; 1, kolumna 2; 2, kolumna 3; minus 1, separator, kolumna 4; 3; wiersz 2; kolumna 1; minus 3, kolumna 2; 3, kolumna 3; minus 1, separator, kolumna 4; 2; wiersz 3; kolumna 1; 2, kolumna 2; 3, kolumna 3; 2, separator, kolumna 4; minus 1; koniec macierzy")?; test("pl", "SimpleSpeak", - expr, "3 na 4 macierz rozszerzona; wiersz 1; kolumna 1; 1, kolumna 2; 2, kolumna 3; minus 1, kolumna 4; 3; wiersz 2; kolumna 1; minus 3, kolumna 2; 3, kolumna 3; minus 1, kolumna 4; 2; wiersz 3; kolumna 1; 2, kolumna 2; 3, kolumna 3; 2, kolumna 4; minus 1; koniec macierzy")?; + expr, "3 na 4 macierz rozszerzona; wiersz 1; kolumna 1; 1, kolumna 2; 2, kolumna 3; minus 1, separator, kolumna 4; 3; wiersz 2; kolumna 1; minus 3, kolumna 2; 3, kolumna 3; minus 1, separator, kolumna 4; 2; wiersz 3; kolumna 1; 2, kolumna 2; 3, kolumna 3; 2, separator, kolumna 4; minus 1; koniec macierzy")?; Ok(()) } @@ -1294,3 +1294,42 @@ fn single_line_with_label() -> Result<()> { expr, "1 równanie, z etykietą 2; b równa się 2")?; return Ok(()); } + +/// Linia pionowa w tabeli jest zapowiadana jako separator (upstream #679). +#[test] +fn dashed_augmented_matrix_separator() -> Result<()> { + let expr = " + + [ + + 123 + + ] + "; + test("pl", "ClearSpeak", expr, "jeden na 3 wiersz macierz; 1, separator, 2, separator, 3")?; + test("pl", "SimpleSpeak", expr, "jeden na 3 wiersz macierz; 1, separator, 2, separator, 3")?; + Ok(()) +} + +/// Linia pozioma wybrzmiewa RAZ, po wierszu, ktory oddziela od nastepnego. +#[test] +fn matrix_row_separator() -> Result<()> { + let expr = " + + [ + + + 1 + 2 + + + 3 + 4 + + + ] + "; + test("pl", "ClearSpeak", expr, "2 na 2 macierz; wiersz 1; 1, 2, separator wiersza; wiersz 2; 3, 4")?; + test("pl", "SimpleSpeak", expr, "2 na 2 macierz; wiersz 1; 1, 2, separator wiersza; wiersz 2; 3, 4")?; + Ok(()) +} From df565e27cdf776249c7f97d37f126c38e6fcfeed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dziwisz?= Date: Thu, 3 Sep 2026 12:59:09 +0200 Subject: [PATCH 086/120] pl: inflect unit names according to the numeral, not with an English "s" (#744) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * pl: mark the unicode translations as verified (t -> T) Mechanical change: 2435 text keys raised from t/ot/ct to T/OT/CT in the two unicode files. No text is touched. The Polish rules were written before the lowercase/uppercase convention was in use, so entries that have been translated all along still carried the "needs review" key. The audit tool therefore reported them as untranslated, which made the real gaps impossible to see: audit-translations pl, untranslated text: 3162 -> 727 The remaining 727 are in the rule files, where the entries need reading rather than a mechanical pass, so they are deliberately left alone. Every raised entry was checked to differ from the English source, i.e. it really is translated. Entries whose text legitimately equals English (Roman numerals, ligatures, proper names, whitespace) were handled separately in the previous PR; entries still holding English text are NOT raised, since that would assert a translation that does not exist. Verification: cargo test --test languages Languages::pl 609 passed, 0 failed diff: 2436 insertions, 2436 deletions, zero changes outside key case The last point is checked by normalising the key case in the diff and confirming no unpaired lines remain (without normalising, 4710 remain, so the check does discriminate). * pl: also mark the multi-line unicode entries as verified The previous commit raised the key on entries written on a single line (`- "±": [t: "..."]`), but not on entries whose body is a block: - "°": - test: if: "parent::*[name(.)='msup' ...]" then: [t: "stopni"] else: [t: "stopień"] Those are exactly the entries carrying inflection or a TTS condition, so 312 of the most deliberate translations in the file were left looking unreviewed. This raises them: 64 in unicode.yaml, 247 in unicode-full.yaml and one single-line entry (U+23E6) missed earlier. One entry is deliberately left with a lower-case key: `t: ""` for U+00A0, the else-branch of the `@data-empty-in-2D` test. That is silence rather than text, and an upper-case key means "a translator checked this wording" - there is no wording to check. English keeps a lower-case key there too. No content changes; only key case. `cargo test --test languages Languages::pl` gives 609 passed, 0 failed, which is the same result as before this commit, as expected for a change the engine treats as equivalent (src/speech.rs matches "t"|"T" in one match arm). * pl: raise the remaining rule-file keys and test the number-set rules Two loose ends in the Polish localization. **Rule-file keys.** 248 entries in the rule files still used a lower-case key while carrying translated Polish text. Content is untouched - every one of the 248 changed lines differs from its predecessor only in the case of the key name, verified line by line after normalizing the key. 17 entries are deliberately left lower-case: five are the letters C, N, Q, R, Z in `dimension-number-sets` (the letter itself is the wording, as in English); `ct: "s"`/`ct: "-"` in the unit rule are the plural suffix and prefix separator; `navigate.yaml:176` is a Polish euphony variant ("ze" before "st-"); the remaining seven are XPath expressions rather than speech. **Tests for the number-set rules.** These rules had no Polish test at all, in a file where two silent defects were already found. New `tests/Languages/pl/number_sets.rs` covers the bare symbols, the dimension form, both sign variants, and ClearSpeak agreeing with SimpleSpeak. The `number_set_with_variable_exponent` case is the negative control: `dimension-number-sets` fires on `count(*)=2`, so a non-numeric exponent must fall through to the full phrase ("liczby całkowite do potęgi n") rather than being read as a dimension. Without it, a rule that swallowed every superscript would still pass. Polish keeps the letter with a dimension ("R 3"), matching en, de, fr, nb, sv, fi and es; Hungarian is the only language that translates in that position too. Measured with the SAPI voice NVDA actually uses: "R 3" is byte-identical to a phonetic "er 3" and "N 4" to "en 4", so the letters are pronounced as their Polish names. cargo test --test languages Languages::pl 614 passed, 0 failed * pl: inflect unit names according to the numeral, not with an English "s" The `unit` rule appended `ct: "s"` whenever a unit had no entry in `PluralForms`, and the Polish `PluralForms` contained no SI base unit at all. Measured output of the engine before this change: 1 metr, 2 metrs, 5 metrs, 21 metrs, 2 grams, 2 sekundas, 2 ampers, 2 mols, 2 kilo-metrs So a Polish NVDA user heard an English plural ending on essentially every quantity. Hungarian avoids this with `CT: ""` (it appends nothing); French and German legitimately use "s"; Russian, whose plurals work like Polish, does not rely on a suffix at all and fills in `PluralForms` instead. Polish was in the wrong group. Polish does not have one plural form. The noun follows the numeral: 1 metr | 2, 3, 4 metry (nominative plural) | 5 metrów (genitive plural) and the teens are the exception that breaks the naive rule: 12, 13, 14 metrów, likewise 112-114, while 22 and 102 take the nominative again. One extra plural column is therefore not enough, so this adds a second definition section, `PluralGenitive`, next to `PluralForms` (now the nominative plural). No Rust change is needed: `build_values` accepts any definition name as a hashmap. The `unit` rule (both the bare and the prefixed branch) and the `currency` rule now read the value of the numeral and pick the section accordingly. When the numeral cannot be determined - a variable coefficient, as in "x ms" - `$Coefficient` is 0 and the genitive is used, which is also what Polish uses for unknown quantities ("x mili-sekund"). Where no inflected form is known the singular is spoken rather than a guessed ending: a wrong ending is worse for a screen reader user than a missing one. Both sections cover all 131 unit and currency names that the Polish `SIUnits`, `UnitsWithoutPrefixes`, `EnglishUnits` and `CurrencySymbols` can produce. The forms are written out because Polish inflection cannot be derived from the ending: metr/metry but litr/litry, om/omy, dzień/dni, tydzień/tygodnie, rok/lata. Multi-word units inflect the head noun and keep the dependent word's case ("5 lat świetlnych", "5 minut łuku"). They were checked against hunspell pl_PL; the only words it does not know are the binary-prefix neologisms (kibibajt, gibibit, ...) whose singular forms already existed in the file. Three base forms were themselves wrong - plurals used as singulars, so the engine said "1 herce" and "1 konie mechaniczne": Hz herce -> herc hp konie mechaniczne -> koń mechaniczny ℧ M h o -> mho Every other language has a singular here (en hertz, ru герц, fi hertsi, es hercio; hp: ru "лошадиная сила", hu "lóerő", nb "hestekraft"; ℧: de/fr "mho"). Why this survived: the existing Polish expectations in tests/Languages/pl/units.rs recorded the broken output, so 609 tests passed while the speech was wrong ("2 dzieńs", "2 tydzieńs", "kwetta-grams"). Expectations harvested from real engine output are a good regression net but prove nothing about correctness unless they are read as a user would hear them. 21 test expectations are updated here, each difference reviewed word by word. Verification: cargo test --test languages Languages::pl 615 passed, 0 failed reverting general.yaml alone -> 6 of 6 new tests fail The new tests in tests/Languages/pl/unit_plurals.rs cover one class of phenomenon each: the three numeral classes including the teens exception, SI base units, the prefixed branch of the rule, irregular nouns, the corrected base forms, and multi-word units. --- Rules/Languages/pl/ClearSpeak_Rules.yaml | 50 +- Rules/Languages/pl/SharedRules/calculus.yaml | 12 +- Rules/Languages/pl/SharedRules/default.yaml | 80 +- Rules/Languages/pl/SharedRules/general.yaml | 189 +- Rules/Languages/pl/SharedRules/geometry.yaml | 14 +- .../pl/SharedRules/linear-algebra.yaml | 2 +- Rules/Languages/pl/definitions.yaml | 286 +- Rules/Languages/pl/navigate.yaml | 152 +- Rules/Languages/pl/overview.yaml | 30 +- Rules/Languages/pl/unicode-full.yaml | 5238 ++++++++--------- Rules/Languages/pl/unicode.yaml | 262 +- tests/Languages/pl.rs | 2 + tests/Languages/pl/ClearSpeak/mfrac.rs | 2 +- tests/Languages/pl/SimpleSpeak/mfrac.rs | 6 +- tests/Languages/pl/number_sets.rs | 69 + tests/Languages/pl/unit_plurals.rs | 86 + tests/Languages/pl/units.rs | 38 +- 17 files changed, 3474 insertions(+), 3044 deletions(-) create mode 100644 tests/Languages/pl/number_sets.rs create mode 100644 tests/Languages/pl/unit_plurals.rs diff --git a/Rules/Languages/pl/ClearSpeak_Rules.yaml b/Rules/Languages/pl/ClearSpeak_Rules.yaml index 5e6cca14c..c7bb0fc7f 100644 --- a/Rules/Languages/pl/ClearSpeak_Rules.yaml +++ b/Rules/Languages/pl/ClearSpeak_Rules.yaml @@ -228,13 +228,13 @@ tag: fraction match: "." replace: - - t: "ułamek z licznikiem" # phrase(the 'fraction with numerator' 6) + - T: "ułamek z licznikiem" # phrase(the 'fraction with numerator' 6) - test: if: "not(IsNode(*[1], 'simple'))" then: [pause: medium] - x: "*[1]" - pause: medium - - t: "i mianownikiem" # phrase(the fraction with numerator 5 'and denominator' 8) + - T: "i mianownikiem" # phrase(the fraction with numerator 5 'and denominator' 8) - x: "*[2]" - pause: long - test: @@ -525,7 +525,7 @@ replace: - test: - if: "count(*)=0" - then: [t: "zbiór pusty"] # phrase('the empty set') + then: [T: "zbiór pusty"] # phrase('the empty set') - else_if: "count(*)=2" then: - test: @@ -537,10 +537,10 @@ - test: if: "$Verbosity!='Terse'" then: [T: ""] # phrase('the' set of all integers) - - t: "zbiór" # phrase(this is a 'set of' numbers) + - T: "zbiór" # phrase(this is a 'set of' numbers) - test: if: "$ClearSpeak_Sets != 'woAll'" - then: [t: "wszystkie"] # phrase(the set of 'all' integers) + then: [T: "wszystkie"] # phrase(the set of 'all' integers) - x: "*[1]/*[1]" - T: "takich że" # phrase(the set S 'such that' x is less than y) - x: "*[1]/*[3]" @@ -645,31 +645,31 @@ - if: "$IsCase" then_test: - if: "$LineCount = 1" - then: [t: "przypadek"] # phrase(this is the first 'case' of three cases) + then: [T: "przypadek"] # phrase(this is the first 'case' of three cases) - else_if: "$Paucal" - then: [t: "przypadki"] # phrase(there are 'two cases' to consider) - else: [t: "przypadków"] # phrase(there are 'five cases' to consider) + then: [T: "przypadki"] # phrase(there are 'two cases' to consider) + else: [T: "przypadków"] # phrase(there are 'five cases' to consider) - else_if: "$IsLine" then_test: - if: "$LineCount = 1" - then: [t: "linia"] # phrase(this is the first 'line' of three lines) + then: [T: "linia"] # phrase(this is the first 'line' of three lines) - else_if: "$Paucal" - then: [t: "linie"] # phrase(there are 'two lines' shown) - else: [t: "linii"] # phrase(there are 'five lines' shown) + then: [T: "linie"] # phrase(there are 'two lines' shown) + else: [T: "linii"] # phrase(there are 'five lines' shown) - else_if: "$ClearSpeak_MultiLineLabel = 'Constraint'" then_test: - if: "$LineCount = 1" - then: [t: "warunek"] # phrase(this is the first 'constraint' of three constraints) + then: [T: "warunek"] # phrase(this is the first 'constraint' of three constraints) - else_if: "$Paucal" - then: [t: "warunki"] # phrase(there are 'two constraints' to satisfy) - else: [t: "warunków"] # phrase(there are 'five constraints' to satisfy) + then: [T: "warunki"] # phrase(there are 'two constraints' to satisfy) + else: [T: "warunków"] # phrase(there are 'five constraints' to satisfy) - else_if: "$ClearSpeak_MultiLineLabel = 'Equation'" then_test: - if: "$LineCount = 1" - then: [t: "równanie"] # phrase(this is the first 'equation' of three equations) + then: [T: "równanie"] # phrase(this is the first 'equation' of three equations) - else_if: "$Paucal" - then: [t: "równania"] # phrase(there are 'two equations' to solve) - else: [t: "równań"] # phrase(there are 'five equations' to solve) + then: [T: "równania"] # phrase(there are 'two equations' to solve) + else: [T: "równań"] # phrase(there are 'five equations' to solve) - else_if: "$ClearSpeak_MultiLineLabel = 'Row'" then_test: - if: "$LineCount = 1" @@ -680,10 +680,10 @@ - else_if: "$ClearSpeak_MultiLineLabel = 'Step'" then_test: - if: "$LineCount = 1" - then: [t: "krok"] # phrase(this is the first 'step' of three steps) + then: [T: "krok"] # phrase(this is the first 'step' of three steps) - else_if: "$Paucal" - then: [t: "kroki"] # phrase(there are 'two steps' to follow) - else: [t: "kroków"] # phrase(there are 'five steps' to follow) + then: [T: "kroki"] # phrase(there are 'two steps' to follow) + else: [T: "kroków"] # phrase(there are 'five steps' to follow) # else 'None -- don't say anything' - pause: short - x: "*" @@ -700,17 +700,17 @@ - pause: medium - test: - if: "($ClearSpeak_MultiLineLabel = 'Auto' and parent::m:piecewise) or $ClearSpeak_MultiLineLabel = 'Case'" - then: [t: "przypadek"] # phrase(in this 'case' x is not equal to y) + then: [T: "przypadek"] # phrase(in this 'case' x is not equal to y) - else_if: "$ClearSpeak_MultiLineLabel = 'Auto' or $ClearSpeak_MultiLineLabel = 'Line'" # already dealt with Auto/Case - then: [t: "linia"] # phrase(the straight 'line' between x and y) + then: [T: "linia"] # phrase(the straight 'line' between x and y) - else_if: "$ClearSpeak_MultiLineLabel = 'Constraint'" - then: [t: "warunek"] # phrase(there is a 'constraint' on possible values) + then: [T: "warunek"] # phrase(there is a 'constraint' on possible values) - else_if: "$ClearSpeak_MultiLineLabel = 'Equation'" - then: [t: "równanie"] # phrase(the 'equation' pi r squared gives the area of a circle) + then: [T: "równanie"] # phrase(the 'equation' pi r squared gives the area of a circle) - else_if: "$ClearSpeak_MultiLineLabel = 'Row'" then: [T: "wiersz"] # phrase(the values on the top 'row' are relevant) - else_if: "$ClearSpeak_MultiLineLabel = 'Step'" - then: [t: "krok"] # phrase(this is a 'step' by step process) + then: [T: "krok"] # phrase(this is a 'step' by step process) # else 'None -- don't say anything' - x: "count(preceding-sibling::*[not(contains(@data-intent-property, ':continued-row:'))]) + 1" - test: diff --git a/Rules/Languages/pl/SharedRules/calculus.yaml b/Rules/Languages/pl/SharedRules/calculus.yaml index 777ca4171..4d499f9a8 100644 --- a/Rules/Languages/pl/SharedRules/calculus.yaml +++ b/Rules/Languages/pl/SharedRules/calculus.yaml @@ -4,7 +4,7 @@ tag: laplacian match: "count(*) <= 1" # can be on ∇^2 or on enclosing mrow replace: - - t: "laplasjan" # phrase('laplacian' of x) -- "LahPlahsian" sounds better with speech engines tested + - T: "laplasjan" # phrase('laplacian' of x) -- "LahPlahsian" sounds better with speech engines tested - test: if: "count(*) = 1" then: @@ -22,8 +22,8 @@ replace: - test: if: "$Verbosity='Terse'" - then: [t: "dywergencja"] # phrase('div' is short for divergence) -- note OneCore voices spell out "div" - else: [t: "dywergencja z"] # phrase('divergence of' this function from the mean) + then: [T: "dywergencja"] # phrase('div' is short for divergence) -- note OneCore voices spell out "div" + else: [T: "dywergencja z"] # phrase('divergence of' this function from the mean) - test: if: "not(IsNode(*[1], 'leaf'))" then: [pause: short] @@ -33,7 +33,7 @@ tag: curl match: "count(*) = 1" replace: - - t: "rotacja" # phrase(the 'curl of' a field) + - T: "rotacja" # phrase(the 'curl of' a field) - test: if: "$Verbosity!='Terse'" then: [T: "z"] # phrase(function 'of' one variable) -- note OneCore voices spell out "div" @@ -48,8 +48,8 @@ replace: - test: if: "$Verbosity!='Terse'" - then: [t: "gradient z"] # phrase(the hill has a 'gradient of' five percent) - else: [t: "gradient"] # phrase(the delete key is labeled 'del') + then: [T: "gradient z"] # phrase(the hill has a 'gradient of' five percent) + else: [T: "gradient"] # phrase(the delete key is labeled 'del') - test: if: "not(IsNode(*[1], 'leaf'))" then: [pause: short] diff --git a/Rules/Languages/pl/SharedRules/default.yaml b/Rules/Languages/pl/SharedRules/default.yaml index 531f57a5d..8397efb7b 100644 --- a/Rules/Languages/pl/SharedRules/default.yaml +++ b/Rules/Languages/pl/SharedRules/default.yaml @@ -100,7 +100,7 @@ tag: ms match: "." replace: - - t: "string" # phrase('the string' is long) + - T: "string" # phrase('the string' is long) - pause: short - x: "text()" @@ -142,7 +142,7 @@ - pause: short - test: if: "$Impairment = 'Blindness'" - then: [t: "koniec ułamka"] # phrase("start of fraction x over y 'end over'") + then: [T: "koniec ułamka"] # phrase("start of fraction x over y 'end over'") - pause: medium @@ -332,7 +332,7 @@ then: - T: "z" # phrase(substitute x 'with' y) - x: "count($Prescripts) div 2" - - t: "lewe indeksy" # phrase(in this equation certain 'prescripts' apply) + - T: "lewe indeksy" # phrase(in this equation certain 'prescripts' apply) - pause: short - test: if: "not($Prescripts[1][self::m:none])" @@ -367,9 +367,9 @@ - test: if: "count($Prescripts) > 4" # give up and just dump them out so at least the content is there then: - - t: "i kolejne lewe indeksy" # phrase(in this case there are values 'and alternating prescripts') + - T: "i kolejne lewe indeksy" # phrase(in this case there are values 'and alternating prescripts') - x: "$Prescripts[position() > 4]" - - t: "koniec lewych indeksów" # phrase(This is where 'end prescripts' occurs) + - T: "koniec lewych indeksów" # phrase(This is where 'end prescripts' occurs) - test: if: "$Postscripts" then: @@ -386,7 +386,7 @@ then: [T: "i"] # phrase(10 is greater than 8 'and' less than 15) - T: "z" # phrase(substitute x 'with' y) - x: "count($Postscripts) div 2" - - t: "prawe indeksy" # phrase(this material includes several 'postscripts') + - T: "prawe indeksy" # phrase(this material includes several 'postscripts') - pause: short - test: if: "not($Postscripts[1][self::m:none])" @@ -452,9 +452,9 @@ - test: if: "count($Postscripts) > 8" # give up and just dump them out so at least the content is there then: - - t: "i kolejne indeksy" # phrase(this situation involves complexities 'and alternating scripts') + - T: "i kolejne indeksy" # phrase(this situation involves complexities 'and alternating scripts') - x: "$Postscripts[position() > 8]" - - t: "koniec indeksów" # phrase(At this point 'end scripts' occurs) + - T: "koniec indeksów" # phrase(At this point 'end scripts' occurs) - name: default tag: mtable @@ -540,7 +540,7 @@ tag: menclose match: "@notation='box' and *[self::m:mtext and .=' ']" replace: - - t: "pusta ramka" # phrase(the 'empty box' contains no values) + - T: "pusta ramka" # 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 @@ -550,29 +550,29 @@ replace: - test: if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' box ')]" - then: [t: "ramka", pause: short] # phrase(the 'box' around the expression) + then: [T: "ramka", pause: short] # phrase(the 'box' around the expression) - test: if: ".[contains(@notation,'roundedbox')]" - then: [t: "zaokrąglona ramka", pause: short] # phrase(the 'round box' around the expression) + then: [T: "zaokrąglona ramka", pause: short] # phrase(the 'round box' around the expression) - test: if: ".[contains(@notation,'circle')]" - then: [t: "okrąg", pause: short] # phrase(the 'circle' around the expression) + then: [T: "okrąg", 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: "linia na" # phrase(draw a straight 'line' on the page) + - T: "linia na" # phrase(draw a straight 'line' on the page) - test: if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' left ')]" - then: [t: "lewo", pause: short] # phrase(line on 'left' of the expression) + then: [T: "lewo", pause: short] # phrase(line on 'left' of the expression) - test: if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' right ')]" - then: [t: "prawo", pause: short] # phrase(line on 'right' of the expression) + then: [T: "prawo", pause: short] # phrase(line on 'right' of the expression) - test: if: ".[contains(@notation,'top')]" - then: [t: "góra", pause: short] # phrase(line on 'top' of the expression) + then: [T: "góra", pause: short] # phrase(line on 'top' of the expression) - test: if: ".[contains(@notation,'bottom')]" - then: [t: "dół", pause: short] # phrase(line on the 'bottom' of the expression) + then: [T: "dół", 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: @@ -582,77 +582,77 @@ else: - test: if: ".[contains(@notation,'updiagonalstrike')]" - then: [t: "ukośnie w górę", pause: short] # phrase(the line runs 'up diagonal') + then: [T: "ukośnie w górę", pause: short] # phrase(the line runs 'up diagonal') - test: if: ".[contains(@notation,'downdiagonalstrike')]" - then: [t: "ukośnie w dół", pause: short] # phrase(the line runs 'down diagonal') + then: [T: "ukośnie w dół", pause: short] # phrase(the line runs 'down diagonal') - test: if: ".[contains(@notation,'verticalstrike')]" - then: [t: "pionowy", pause: short] # phrase(the line is 'vertical') + then: [T: "pionowy", pause: short] # phrase(the line is 'vertical') - test: if: ".[contains(@notation,'horizontalstrike')]" - then: [t: "poziomy", pause: short] # phrase(the line is 'horizontal') - - t: "krzyż na zewnątrz" # phrase(please 'cross out' the incorrect answer) + then: [T: "poziomy", pause: short] # phrase(the line is 'horizontal') + - T: "krzyż na zewnątrz" # phrase(please 'cross out' the incorrect answer) - pause: short - test: if: ".[contains(@notation,'uparrow')]" - then: [t: "strzałka w górę", pause: short] # phrase(direction is shown by the 'up arrow') + then: [T: "strzałka w górę", pause: short] # phrase(direction is shown by the 'up arrow') - test: if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' downarrow ')]" - then: [t: "strzałka w dół", pause: short] # phrase(the trend is shown by the 'down arrow') + then: [T: "strzałka w dół", pause: short] # phrase(the trend is shown by the 'down arrow') - test: if: ".[contains(@notation,'leftarrow')]" - then: [t: "strzałka w lewo", pause: short] # phrase(the 'left arrow' indicates going back) + then: [T: "strzałka w lewo", pause: short] # phrase(the 'left arrow' indicates going back) - test: if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' rightarrow ')]" - then: [t: "strzałka w prawo", pause: short] # phrase(the 'right arrow' indicates moving forward) + then: [T: "strzałka w prawo", pause: short] # phrase(the 'right arrow' indicates moving forward) - test: if: ".[contains(@notation,'northeastarrow')]" - then: [t: "strzałka na północny wschód", pause: short] # phrase(direction is indicated by the 'northeast arrow') + then: [T: "strzałka na północny wschód", pause: short] # phrase(direction is indicated by the 'northeast arrow') - test: if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' southeastarrow ')]" - then: [t: "strzałka na południowy wschód", pause: short] # phrase(direction is shown by the 'southeast arrow') + then: [T: "strzałka na południowy wschód", pause: short] # phrase(direction is shown by the 'southeast arrow') - test: if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' southwestarrow ')]" - then: [t: "strzałka na południowy zachód", pause: short] # phrase(direction is shown by the 'southwest arrow') + then: [T: "strzałka na południowy zachód", pause: short] # phrase(direction is shown by the 'southwest arrow') - test: if: ".[contains(@notation,'northwestarrow')]" - then: [t: "strzałka na północny zachód", pause: short] # phrase(direction is shown by the 'northwest arrow') + then: [T: "strzałka na północny zachód", pause: short] # phrase(direction is shown by the 'northwest arrow') - test: if: ".[contains(@notation,'updownarrow')]" - then: [t: "strzałka dwukierunkowa pionowa", pause: short] # phrase(upward movement is indicated by the 'double ended vertical arrow') + then: [T: "strzałka dwukierunkowa pionowa", pause: short] # phrase(upward movement is indicated by the 'double ended vertical arrow') - test: if: ".[contains(@notation,'leftrightarrow')]" - then: [t: "strzałka dwukierunkowa pozioma", pause: short] # phrase(progress is indicated by the 'double ended horizontal arrow') + then: [T: "strzałka dwukierunkowa pozioma", pause: short] # phrase(progress is indicated by the 'double ended horizontal arrow') - test: if: ".[contains(@notation,'northeastsouthwestarrow')]" - then: [t: "strzałka dwukierunkowa ukośna w górę", pause: short] # phrase(trend is indicated by the 'double ended up diagonal arrow') + then: [T: "strzałka dwukierunkowa ukośna w górę", pause: short] # phrase(trend is indicated by the 'double ended up diagonal arrow') - test: if: ".[contains(@notation,'northwestsoutheastarrow')]" - then: [t: "strzałka dwukierunkowa ukośna w dół", pause: short] # phrase(trend is indicated by the 'double ended down diagonal arrow') + then: [T: "strzałka dwukierunkowa ukośna w dół", pause: short] # phrase(trend is indicated by the 'double ended down diagonal arrow') - test: if: ".[contains(@notation,'actuarial')]" - then: [t: "symbol aktuarialny", pause: short] # phrase(the 'actuarial symbol' represents a specific quantity) + then: [T: "symbol aktuarialny", pause: short] # phrase(the 'actuarial symbol' represents a specific quantity) - test: if: ".[contains(@notation,'madrub')]" - then: [t: "arabski symbol silni", pause: short] # phrase(the 'arabic factorial symbol' represents a factorial operation) + then: [T: "arabski symbol silni", pause: short] # phrase(the 'arabic factorial symbol' represents a factorial operation) - test: if: ".[contains(@notation,'phasorangle')]" - then: [t: "kąt fazowy", pause: short] # phrase(the 'phasor angle' is used to measure electrical current) + then: [T: "kąt fazowy", 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: "znak dzielenia pisemnego", pause: short] # phrase(the 'long division symbol' indicates a long division calculation) + then: [T: "znak dzielenia pisemnego", pause: short] # phrase(the 'long division symbol' indicates a long division calculation) - test: if: ".[contains(@notation,'radical')]" then: [T: "pierwiastek kwadratowy", pause: short] # phrase(5 is the 'square root' of 25) - - t: "obejmujący" # phrase(parentheses are 'enclosing' part of the equation) + - T: "obejmujący" # phrase(parentheses are 'enclosing' part of the equation) - test: if: "*[self::m:mtext and .=' ']" then: [T: "spacja"] # 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: "koniec obramowania"] # phrase(reached the 'end enclosure' point) + then: [T: "koniec obramowania"] # phrase(reached the 'end enclosure' point) - pause: short - name: semantics diff --git a/Rules/Languages/pl/SharedRules/general.yaml b/Rules/Languages/pl/SharedRules/general.yaml index 40e996f49..f3d026f2b 100644 --- a/Rules/Languages/pl/SharedRules/general.yaml +++ b/Rules/Languages/pl/SharedRules/general.yaml @@ -17,15 +17,15 @@ - bookmark: "*[1]/@id" - test: - if: "*[1][.='ℂ']" - then: [t: "liczby zespolone"] # phrase('complex numbers' consist of two parts) + then: [T: "liczby zespolone"] # phrase('complex numbers' consist of two parts) - else_if: "*[1][.='ℕ']" - then: [t: "liczby naturalne"] # phrase('natural numbers' are numbers from 1 to infinity) + then: [T: "liczby naturalne"] # phrase('natural numbers' are numbers from 1 to infinity) - else_if: "*[1][.='ℚ']" - then: [t: "liczby wymierne"] # phrase('rational numbers' are the fraction of 2 integers) + then: [T: "liczby wymierne"] # phrase('rational numbers' are the fraction of 2 integers) - else_if: "*[1][.='ℝ']" - then: [t: "liczby rzeczywiste"] # phrase('real numbers' can be both positive and negative) + then: [T: "liczby rzeczywiste"] # phrase('real numbers' can be both positive and negative) - else_if: "*[1][.='ℤ']" - then: [t: "liczby całkowite"] # phrase(positive 'integers' are natural numbers above 0) + then: [T: "liczby całkowite"] # phrase(positive 'integers' are natural numbers above 0) else: [x: "*[1][text()]"] # shouldn't happen - name: dimension-number-sets @@ -57,15 +57,15 @@ - bookmark: "@id" - test: - if: ".='ℂ'" - then: [t: "liczby zespolone"] # phrase('the complex numbers' include 2 parts) + then: [T: "liczby zespolone"] # phrase('the complex numbers' include 2 parts) - else_if: ".='ℕ'" - then: [t: "liczby naturalne"] # phrase('the natural numbers' begin at 1) + then: [T: "liczby naturalne"] # phrase('the natural numbers' begin at 1) - else_if: ".='ℚ'" - then: [t: "liczby wymierne"] # phrase('the rational numbers' are the fraction of 2 integers) + then: [T: "liczby wymierne"] # phrase('the rational numbers' are the fraction of 2 integers) - else_if: ".='ℝ'" - then: [t: "liczby rzeczywiste"] # phrase('the real numbers' can be both positive and negative) + then: [T: "liczby rzeczywiste"] # phrase('the real numbers' can be both positive and negative) - else_if: ".='ℤ'" - then: [t: "liczby całkowite"] # phrase('the integers' are natural numbers above 0) + then: [T: "liczby całkowite"] # phrase('the integers' are natural numbers above 0) else: [x: "text()"] # shouldn't happen - name: word-degree-celsius-fahrenheit-unit @@ -101,14 +101,14 @@ match: "." replace: - bookmark: "@id" - - t: "część rzeczywista" # phrase('the real part' of a complex number does not include the imaginary part) + - T: "część rzeczywista" # phrase('the real part' of a complex number does not include the imaginary part) - name: imaginary-part tag: imaginary-part match: "." replace: - bookmark: "@id" - - t: "część urojona" # phrase('the imaginary part' is part of a complex number) + - T: "część urojona" # phrase('the imaginary part' is part of a complex number) # rules on scripted vertical bars ('evaluated at') - name: evaluated-at-2 @@ -117,7 +117,7 @@ replace: - x: "*[1]" - pause: auto - - t: "obliczone w" # phrase(results were 'evaluated at' a given point) + - T: "obliczone w" # phrase(results were 'evaluated at' a given point) - pause: auto - x: "*[2]" @@ -127,10 +127,10 @@ replace: - x: "*[1]" - pause: auto - - t: "obliczone w" # phrase(results were 'evaluated at' this point) + - T: "obliczone w" # phrase(results were 'evaluated at' this point) - pause: auto - x: "*[3]" - - t: "minus to samo wyrażenie obliczone w" # phrase(this result is 'minus the same expression evaluated at' an earlier point) + - T: "minus to samo wyrażenie obliczone w" # phrase(this result is 'minus the same expression evaluated at' an earlier point) - x: "*[2]" - name: permutation @@ -139,7 +139,7 @@ match: "count(*)=2 and contains(@data-intent-property, ':infix:')" replace: - x: "*[2]" - - t: "permutacji z" # phrase(the solution involves several 'permutations of' values) + - T: "permutacji z" # phrase(the solution involves several 'permutations of' values) - x: "*[1]" - name: intervals @@ -221,7 +221,7 @@ match: "." replace: - x: "*[1]" - - t: "z okresem" # phrase('with repeating digits') + - T: "z okresem" # phrase('with repeating digits') - spell: "*[2]" - name: msubsup-skip-super @@ -286,6 +286,15 @@ 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')])" + # Polish plural depends on the numeral, not only on "is it one": + # 1 metr | 2, 3, 4 metry (nominative plural) | 5 metrów (genitive plural) + # and the teens break the naive rule: 12, 13, 14 metrów (genitive, despite + # ending in 2, 3, 4), likewise 112-114. $Coefficient is 0 when the numeral + # cannot be determined, which falls through to the genitive - the form + # Polish uses with unknown or large quantities. + - Coefficient: "IfThenElse($MRowForCoefficient, number($MRowForCoefficient/*[1][self::m:mn]), 0)" + - IsNominativePlural: "$Coefficient mod 10 >= 2 and $Coefficient mod 10 <= 4 and + not($Coefficient mod 100 >= 12 and $Coefficient mod 100 <= 14)" - Prefix: "''" - Word: "''" replace: @@ -333,9 +342,13 @@ - if: "$IsSingular" # HACK: '\uF8FE' is used internally for the concatenation char by 'ct' -- this gets the prefix concatenated to the base then: [x: "$Word"] - - else_if: "DefinitionValue($Word, 'Speech', 'PluralForms') != ''" + - else_if: "$IsNominativePlural and DefinitionValue($Word, 'Speech', 'PluralForms') != ''" then: [x: "DefinitionValue($Word, 'Speech', 'PluralForms')"] - else: [x: "$Word", ct: "s"] + - else_if: "DefinitionValue($Word, 'Speech', 'PluralGenitive') != ''" + then: [x: "DefinitionValue($Word, 'Speech', 'PluralGenitive')"] + # No inflected form is known: speak the singular, because a wrong + # ending is worse for a screen reader user than a missing one. + else: [x: "$Word"] else: - x: "$Prefix" - ct: "-" @@ -343,9 +356,11 @@ - if: "$IsSingular" # HACK: '\uF8FE' is used internally for the concatenation char by 'ct' -- this gets the prefix concatenated to the base then: [x: "concat('\uF8FE', $Word)"] - - else_if: "DefinitionValue($Word, 'Speech', 'PluralForms') != ''" + - else_if: "$IsNominativePlural and DefinitionValue($Word, 'Speech', 'PluralForms') != ''" then: [x: "concat('\uF8FE', DefinitionValue($Word, 'Speech', 'PluralForms'))"] - else: [x: "concat('\uF8FE', $Word)", ct: "s"] + - else_if: "DefinitionValue($Word, 'Speech', 'PluralGenitive') != ''" + then: [x: "concat('\uF8FE', DefinitionValue($Word, 'Speech', 'PluralGenitive'))"] + else: [x: "concat('\uF8FE', $Word)"] # need to reverse the order of speech: $ 3 -> 3 dollars - name: currency @@ -355,15 +370,21 @@ # 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')" + # Same numeral-dependent choice as in the 'unit' rule above. + - Coefficient: "IfThenElse(*[3][self::m:mn], number(*[3]), 0)" + - IsNominativePlural: "$Coefficient mod 10 >= 2 and $Coefficient mod 10 <= 4 and + not($Coefficient mod 100 >= 12 and $Coefficient mod 100 <= 14)" replace: - bookmark: "*[3]/@id" - x: "*[3]" - test: - if: "$IsSingular" then: [x: "$CurrencyWord"] - - else_if: "DefinitionValue($CurrencyWord, 'Speech', 'PluralForms') != ''" + - else_if: "$IsNominativePlural and DefinitionValue($CurrencyWord, 'Speech', 'PluralForms') != ''" then: [x: "DefinitionValue($CurrencyWord, 'Speech', 'PluralForms')"] - else: [x: "DefinitionValue(*[1], 'Speech', 'CurrencySymbols')", ct: "s"] + - else_if: "DefinitionValue($CurrencyWord, 'Speech', 'PluralGenitive') != ''" + then: [x: "DefinitionValue($CurrencyWord, 'Speech', 'PluralGenitive')"] + else: [x: "$CurrencyWord"] - name: sin tag: mi @@ -378,7 +399,7 @@ - bookmark: "@id" - test: if: "$Verbosity='Terse'" - then: [t: "cosinus"] # phrase('cos' is the abbreviation for cosine) + then: [T: "cosinus"] # phrase('cos' is the abbreviation for cosine) else: [T: "cosinus"] # phrase(find the 'cosine' in a right-angle triangle) - name: tan tag: mi @@ -387,7 +408,7 @@ - bookmark: "@id" - test: if: "$Verbosity='Terse'" - then: [t: "tangens"] # phrase(the 'tan' is the ratio of the opposite to the adjacent side of a right-angled triangle) + then: [T: "tangens"] # phrase(the 'tan' is the ratio of the opposite to the adjacent side of a right-angled triangle) else: [T: "tangens"] # phrase(a 'tangent' is a straight line that touches a curve) - name: sec tag: mi @@ -396,8 +417,8 @@ - bookmark: "@id" - test: if: "$Verbosity='Terse'" - then: [t: "sekans"] # phrase(to 'seek' a solution) - else: [t: "sekans"] # phrase(a 'secant' intersects a curve at two or more points) + then: [T: "sekans"] # phrase(to 'seek' a solution) + else: [T: "sekans"] # phrase(a 'secant' intersects a curve at two or more points) - name: csc tag: mi match: ".='csc'" @@ -405,8 +426,8 @@ - bookmark: "@id" - test: if: "$Verbosity='Terse'" - then: [t: "kosekans"] # phrase(we will 'cosecant' a solution) - else: [t: "kosekans"] # phrase(the 'cosecant' is the reciprocal of the secant) + then: [T: "kosekans"] # phrase(we will 'cosecant' a solution) + else: [T: "kosekans"] # phrase(the 'cosecant' is the reciprocal of the secant) - name: cot tag: mi match: ".='cot'" @@ -414,8 +435,8 @@ - bookmark: "@id" - test: if: "$Verbosity='Terse'" - then: [t: "cotangens"] # phrase(find the 'cotangent' in a right-angle triangle) - else: [t: "cotangens"] # phrase(the 'cotangent' is the reciprocal of the tangent) + then: [T: "cotangens"] # phrase(find the 'cotangent' in a right-angle triangle) + else: [T: "cotangens"] # phrase(the 'cotangent' is the reciprocal of the tangent) - name: sinh tag: mi @@ -424,8 +445,8 @@ - bookmark: "@id" - test: if: "$Verbosity='Terse'" - then: [t: "sinus hiperboliczny"] # phrase(the word 'sinch' is an abbreviation for hyperbolic sine) - else: [t: "sinus hiperboliczny"] # phrase(the 'hyperbolic sine' is used in mathematics) + then: [T: "sinus hiperboliczny"] # phrase(the word 'sinch' is an abbreviation for hyperbolic sine) + else: [T: "sinus hiperboliczny"] # phrase(the 'hyperbolic sine' is used in mathematics) - name: cosh tag: mi match: ".='cosh'" @@ -433,8 +454,8 @@ - bookmark: "@id" - test: if: "$Verbosity='Terse'" - then: [t: "cosinus hiperboliczny"] # phrase('cosh' is an abbreviation of hyperbolic cosine) - else: [t: "cosinus hiperboliczny"] # phrase(the 'hyperbolic cosine' is a mathematical function) + then: [T: "cosinus hiperboliczny"] # phrase('cosh' is an abbreviation of hyperbolic cosine) + else: [T: "cosinus hiperboliczny"] # phrase(the 'hyperbolic cosine' is a mathematical function) - name: tanh tag: mi match: ".='tanh'" @@ -442,8 +463,8 @@ - bookmark: "@id" - test: if: "$Verbosity='Terse'" - then: [t: "tangens hiperboliczny"] # phrase('tanch' is shorthand for hyperbolic tangent) - else: [t: "tangens hiperboliczny"] # phrase('hyperbolic tangent' is a mathematical function) + then: [T: "tangens hiperboliczny"] # phrase('tanch' is shorthand for hyperbolic tangent) + else: [T: "tangens hiperboliczny"] # phrase('hyperbolic tangent' is a mathematical function) - name: sech tag: mi match: ".='sech'" @@ -451,8 +472,8 @@ - bookmark: "@id" - test: if: "$Verbosity='Terse'" - then: [t: "sekans hiperboliczny"] # phrase('sheck' is shorthand for hyperbolic secant) - else: [t: "sekans hiperboliczny"] # phrase('hyperbolic secant' is a mathematical function) + then: [T: "sekans hiperboliczny"] # phrase('sheck' is shorthand for hyperbolic secant) + else: [T: "sekans hiperboliczny"] # phrase('hyperbolic secant' is a mathematical function) - name: csch tag: mi match: ".='csch'" @@ -460,8 +481,8 @@ - bookmark: "@id" - test: if: "$Verbosity='Terse'" - then: [t: "kosekans hiperboliczny"] # phrase('cosheck' is shorthand for hyperbolic cosecant) - else: [t: "kosekans hiperboliczny"] # phrase('hyperbolic cosecant' is a mathematical function) + then: [T: "kosekans hiperboliczny"] # phrase('cosheck' is shorthand for hyperbolic cosecant) + else: [T: "kosekans hiperboliczny"] # phrase('hyperbolic cosecant' is a mathematical function) - name: coth tag: mi match: ".='coth'" @@ -469,8 +490,8 @@ - bookmark: "@id" - test: if: "$Verbosity='Terse'" - then: [t: "cotangens hiperboliczny"] # phrase('cotanch' is shorthand for hyperbolic cotangent) - else: [t: "cotangens hiperboliczny"] # phrase(the 'hyperbolic cotangent' is a mathematical function) + then: [T: "cotangens hiperboliczny"] # phrase('cotanch' is shorthand for hyperbolic cotangent) + else: [T: "cotangens hiperboliczny"] # phrase(the 'hyperbolic cotangent' is a mathematical function) - name: exponential tag: mi match: ".='exp'" @@ -478,8 +499,8 @@ - bookmark: "@id" - test: if: "$Verbosity='Terse'" - then: [t: "eksponenta"] # phrase('exp' means exponential function) - else: [t: "funkcja wykładnicza"] # phrase('exponential' function) + then: [T: "eksponenta"] # phrase('exp' means exponential function) + else: [T: "funkcja wykładnicza"] # phrase('exponential' function) - name: covariance tag: mi match: ".='Cov'" @@ -487,8 +508,8 @@ - bookmark: "@id" - test: if: "$Verbosity='Terse'" - then: [t: "kowariancja"] # phrase('Cov' is shorthand for the covariance function) - else: [t: "kowariancja"] # phrase('covariance' function) + then: [T: "kowariancja"] # phrase('Cov' is shorthand for the covariance function) + else: [T: "kowariancja"] # phrase('covariance' function) - name: maximum tag: mi @@ -551,25 +572,25 @@ then: - test: - if: "self::m:piecewise" - then: [t: "przypadek"] # phrase(this is the first 'case' of three cases) + then: [T: "przypadek"] # phrase(this is the first 'case' of three cases) - else_if: "self::m:system-of-equations" - then: [t: "równanie"] # phrase(this is the first 'equation' of three equations) - else: [t: "linia"] # phrase(this is the first 'line' of three lines) + then: [T: "równanie"] # phrase(this is the first 'equation' of three equations) + else: [T: "linia"] # phrase(this is the first 'line' of three lines) - else_if: "($LineCount mod 10 = 2 or $LineCount mod 10 = 3 or $LineCount mod 10 = 4) and not($LineCount mod 100 = 12 or $LineCount mod 100 = 13 or $LineCount mod 100 = 14)" then: - test: - if: "self::m:piecewise" - then: [t: "przypadki"] # phrase(there are 'two cases' to consider) + then: [T: "przypadki"] # phrase(there are 'two cases' to consider) - else_if: "self::m:system-of-equations" - then: [t: "równania"] # phrase(there are 'two equations' to solve) - else: [t: "linie"] # phrase(there are 'two lines' shown) + then: [T: "równania"] # phrase(there are 'two equations' to solve) + else: [T: "linie"] # phrase(there are 'two lines' shown) - else: - test: - if: "self::m:piecewise" - then: [t: "przypadków"] # phrase(there are 'five cases' to consider) + then: [T: "przypadków"] # phrase(there are 'five cases' to consider) - else_if: "self::m:system-of-equations" - then: [t: "równań"] # phrase(there are 'five equations' to solve) - else: [t: "linii"] # phrase(there are 'five lines' shown) + then: [T: "równań"] # phrase(there are 'five equations' to solve) + else: [T: "linii"] # phrase(there are 'five lines' shown) - pause: short - x: "*" - pause: long @@ -586,10 +607,10 @@ - pause: medium - test: - if: "parent::m:piecewise" - then: [t: "przypadek"] # phrase('case' 1 of 10 cases) + then: [T: "przypadek"] # phrase('case' 1 of 10 cases) - else_if: "parent::m:system-of-equations" - then: [t: "równanie"] # phrase('equation' 1 of 10 equations) - else: [t: "linia"] # phrase('line 1 of 10 lines) + then: [T: "równanie"] # phrase('equation' 1 of 10 equations) + else: [T: "linia"] # phrase('line 1 of 10 lines) - x: "count(preceding-sibling::*[not(contains(@data-intent-property, ':continued-row:'))]) + 1" - test: if: "self::m:mlabeledtr" @@ -638,13 +659,13 @@ variables: [IsColumnSilent: true()] match: "count(*)=1 and *[self::m:mtr][count(*) = 1]" replace: - - t: "jeden na jeden" # phrase(the '1 by 1' matrix) + - T: "jeden na jeden" # 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: "wyznacznik"] # phrase(the 2 by 2 'determinant')) else: [T: "macierz"] # phrase(the 2 by 2 'matrix') - - t: "z elementem" # phrase(the 2 by 2 matrix 'with entry' x) + - T: "z elementem" # phrase(the 2 by 2 matrix 'with entry' x) - x: "*[1]/*" # simpler reading methods for special case matrices @@ -657,7 +678,7 @@ - x: count(*) - T: "na" # phrase(the 1 'by' 2 matrix) - x: count(*[self::m:mtr][1]/*) - - t: "macierz zerowa" # phrase(the 2 by 2 'zero matrix') + - T: "macierz zerowa" # phrase(the 2 by 2 'zero matrix') - pause: long - name: identity-matrix @@ -672,7 +693,7 @@ - x: count(*) - T: "na" # phrase(the 1 'by' 2 matrix) - x: count(*[self::m:mtr][1]/*) - - t: "macierz jednostkowa" # phrase(the 2 by 2 'identity matrix') + - T: "macierz jednostkowa" # phrase(the 2 by 2 'identity matrix') - pause: long - name: diagonal-matrix @@ -689,7 +710,7 @@ - x: count(*) - T: "na" # phrase(the 1 'by' 2 matrix) - x: count(*[self::m:mtr][1]/*) - - t: "macierz diagonalna" # phrase(the 2 by 2 'diagonal matrix') + - T: "macierz diagonalna" # 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 @@ -711,7 +732,7 @@ replace: - T: "" # phrase('the' 2 by 2 matrix M) - x: count(*) - - t: "na jedną kolumnę" # phrase(the 2 'by 1 column' matrix) + - T: "na jedną kolumnę" # phrase(the 2 'by 1 column' matrix) - test: if: "$ClearSpeak_Matrix = 'Vector' or $ClearSpeak_Matrix = 'EndVector'" then: [T: "wektor"] # phrase(the 2 by 2 'vector') @@ -733,7 +754,7 @@ replace: - T: "" # phrase('the' 2 by 2 matrix M) - x: "count(*)" - - t: "na jedną kolumnę" # phrase(the 2 'by 1 column' matrix) + - T: "na jedną kolumnę" # phrase(the 2 'by 1 column' matrix) - test: if: "$ClearSpeak_Matrix = 'Vector' or $ClearSpeak_Matrix = 'EndVector'" then: [T: "wektor"] # phrase(the 2 column 'vector') @@ -742,7 +763,7 @@ - x: "*" # select the rows (mtr) - test: if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" - then: [t: "koniec macierzy"] # phrase(the 'end of matrix' has been reached) + then: [T: "koniec macierzy"] # phrase(the 'end of matrix' has been reached) - name: 1x2-or-3-matrix tag: matrix @@ -753,7 +774,7 @@ - count(*[1]/*)<=3 and # at least two cols - IsNode(*/*/*,'simple') # IsNode() returns true if all the nodes are simple replace: - - t: "jeden na" # phrase('the 1 by' 2 matrix) + - T: "jeden na" # phrase('the 1 by' 2 matrix) - x: count(*/*) - T: "wiersz" # phrase(the 1 by 4 'row' matrix) - test: @@ -775,7 +796,7 @@ variables: [IsColumnSilent: "$SpeechStyle = 'ClearSpeak' and $ClearSpeak_Matrix = 'SilentColNum'"] match: "count(*)=1" # one row replace: - - t: "jeden na" # phrase('the 1 by' 2 matrix) + - T: "jeden na" # phrase('the 1 by' 2 matrix) - x: "count(*/*)" - T: "wiersz" # phrase(the 1 by 2 'row' matrix) - test: @@ -811,7 +832,7 @@ else: - test: if: "@columnlines and (contains(normalize-space(@columnlines), 'solid') or contains(normalize-space(@columnlines), 'dashed'))" - then: [t: "macierz rozszerzona"] # phrase(the 2 by 2 'augmented matrix') + then: [T: "macierz rozszerzona"] # phrase(the 2 by 2 'augmented matrix') else: [T: "macierz"] # phrase(the 2 by 2 'matrix') - pause: long - x: "*" @@ -838,7 +859,7 @@ else: - test: if: "@columnlines and (contains(normalize-space(@columnlines), 'solid') or contains(normalize-space(@columnlines), 'dashed'))" - then: [t: "macierz rozszerzona"] # phrase(the 2 by 2 'augmented matrix') + then: [T: "macierz rozszerzona"] # phrase(the 2 by 2 'augmented matrix') else: [T: "macierz"] # phrase(the 2 by 2 'matrix') - pause: long - x: "*" @@ -1077,12 +1098,12 @@ - bookmark: "*[1]/@id" - test: - if: ".='s'" - then: [t: "ciało stałe"] # phrase(Boron is a 'solid' in its natural state) + then: [T: "ciało stałe"] # phrase(Boron is a 'solid' in its natural state) - else_if: ".='l'" - then: [t: "ciecz"] # phrase(water is a 'liquid') + then: [T: "ciecz"] # phrase(water is a 'liquid') - else_if: ".='g'" - then: [t: "gaz"] # phrase(hydrogen is a 'gas' ) - else: [t: "wodny"] # phrase(an 'aqueous' solution is contained in water) + then: [T: "gaz"] # phrase(hydrogen is a 'gas' ) + else: [T: "wodny"] # phrase(an 'aqueous' solution is contained in water) - pause: short - name: chemical-formula-operator-bond @@ -1093,13 +1114,13 @@ - bookmark: "@id" - test: - if: ".='-' or .=':'" - then: [t: "wiązanie pojedyncze"] # phrase(a 'single bond' is formed when two atoms share one pair of electrons) + then: [T: "wiązanie pojedyncze"] # phrase(a 'single bond' is formed when two atoms share one pair of electrons) - else_if: ".='=' or .='∷'" - then: [t: "wiązanie podwójne"] # phrase(a 'double bond' may occur when two atoms share two pairs of electrons) + then: [T: "wiązanie podwójne"] # phrase(a 'double bond' may occur when two atoms share two pairs of electrons) - else_if: ".='≡'" - then: [t: "wiązanie potrójne"] # phrase(a 'triple bond' occurs when two atoms share three pairs of electrons) + then: [T: "wiązanie potrójne"] # phrase(a 'triple bond' occurs when two atoms share three pairs of electrons) - else_if: ".='≣'" - then: [t: "wiązanie poczwórne"] # phrase(a 'quadruple bond' occurs when two atoms share four pairs of electrons) + then: [T: "wiązanie poczwórne"] # phrase(a 'quadruple bond' occurs when two atoms share four pairs of electrons) else: [x: "text()"] - name: chemical-formula-operator @@ -1118,14 +1139,14 @@ - if: ".='→' or .='⟶'" then_test: if: "$Verbosity='Terse'" - then: [t: "tworzy"] # phrase(hydrogen and oxygen 'forms' water ) - else: [t: "reaguje tworząc"] # phrase(hydrogen and oxygen 'reacts to form' water) + then: [T: "tworzy"] # phrase(hydrogen and oxygen 'forms' water ) + else: [T: "reaguje tworząc"] # phrase(hydrogen and oxygen 'reacts to form' water) - else_if: ".='⇌' or .='🣑'" # U+01F8D1 - then: [t: "jest w równowadze z"] # phrase(a reactant 'is in equilibrium with' a product) + then: [T: "jest w równowadze z"] # phrase(a reactant 'is in equilibrium with' a product) - else_if: ".='🣓'" # U+1F8D3 - then: [t: "jest w równowadze przesuniętej w lewo z"] # phrase(the reactant 'is in equilibrium biased to the left with' the product) + then: [T: "jest w równowadze przesuniętej w lewo z"] # phrase(the reactant 'is in equilibrium biased to the left with' the product) - else_if: ".='🣒'" # U+1F8D2 - then: [t: "jest w równowadze przesuniętej w prawo z"] # phrase(the reactant 'is in equilibrium biased to the right with' the product) + then: [T: "jest w równowadze przesuniętej w prawo z"] # phrase(the reactant 'is in equilibrium biased to the right with' the product) else: [x: "*"] - name: chemical-equation-operator diff --git a/Rules/Languages/pl/SharedRules/geometry.yaml b/Rules/Languages/pl/SharedRules/geometry.yaml index f110a71ed..3fa9c9ccc 100644 --- a/Rules/Languages/pl/SharedRules/geometry.yaml +++ b/Rules/Languages/pl/SharedRules/geometry.yaml @@ -7,12 +7,12 @@ - test: if: "$Verbosity='Verbose'" then: - - t: "odcinek od" # phrase('the line segment from' A to B) + - T: "odcinek od" # phrase('the line segment from' A to B) - x: "*[1]" - T: "do" # phrase(the line segment from A 'to' B) - x: "*[2]" else: - - t: "odcinek" # phrase(the 'line segment' A B) + - T: "odcinek" # phrase(the 'line segment' A B) - x: "*[1]" - x: "*[2]" @@ -23,12 +23,12 @@ - test: if: "$Verbosity='Verbose'" then: - - t: "półprosta od" # phrase('the ray from' A to B) + - T: "półprosta od" # phrase('the ray from' A to B) - x: "*[1]" - T: "do" # phrase(the ray from A 'to' B) - x: "*[2]" else: - - t: "półprosta" # phrase(the 'ray'A B) + - T: "półprosta" # phrase(the 'ray'A B) - x: "*[1]" - x: "*[2]" @@ -47,9 +47,9 @@ - test: if: "$Verbosity='Verbose'" then: - - t: "miara kąta" # phrase('the measure of the angle' ABC) + - T: "miara kąta" # phrase('the measure of the angle' ABC) else: - - t: "miara kąta" # phrase('measure of angle' ABC) + - T: "miara kąta" # phrase('measure of angle' ABC) - x: "*[1]" - x: "*[2]" - x: "*[3]" @@ -74,4 +74,4 @@ - pause: short - test: if: "($SpeechStyle='ClearSpeak' and $Verbosity='Verbose') or not(IsNode(*[last()],'leaf'))" - then: [t: "punkt końcowy"] # phrase(start point, 'end point') + then: [T: "punkt końcowy"] # phrase(start point, 'end point') diff --git a/Rules/Languages/pl/SharedRules/linear-algebra.yaml b/Rules/Languages/pl/SharedRules/linear-algebra.yaml index 1ebcf9f95..69752bd48 100644 --- a/Rules/Languages/pl/SharedRules/linear-algebra.yaml +++ b/Rules/Languages/pl/SharedRules/linear-algebra.yaml @@ -36,7 +36,7 @@ - x: "*[1]" - test: if: "not(IsNode(*[1], 'simple')) and $Impairment = 'Blindness'" - then: [t: "koniec wyznacznika"] # phrase('end determinant' of a matrix) + then: [T: "koniec wyznacznika"] # phrase('end determinant' of a matrix) - name: subscripted-norm diff --git a/Rules/Languages/pl/definitions.yaml b/Rules/Languages/pl/definitions.yaml index 9c4c6e69c..18785562b 100644 --- a/Rules/Languages/pl/definitions.yaml +++ b/Rules/Languages/pl/definitions.yaml @@ -363,7 +363,7 @@ "F": "farad", "Gy": "grej", "H": "henr", - "Hz": "herce", + "Hz": "herc", "J": "dżul", "kat": "katal", "lm": "lumen", @@ -438,7 +438,7 @@ "M": "molowy", "R": "rentgen", "rpm": "obrót na minutę", - "℧": "M h o", + "℧": "mho", "dyn": "dyna", "erg": "erg", @@ -495,7 +495,7 @@ "lb t": "funt trojański", # energy - "hp": "konie mechaniczne", + "hp": "koń mechaniczny", "BTU": "btu", "°F": "stopień Fahrenheita", "℉": "stopień Fahrenheita", @@ -511,25 +511,277 @@ } - PluralForms: { - # FIX: this needs to be flushed out - "cal": "cale", "cal kwadratowy": "cale kwadratowe", "cal sześcienny": "cale sześcienne", - "stopa": "stopy", "stopa kwadratowa": "stopy kwadratowe", "stopa sześcienna": "stopy sześcienne", + # Nominative plural - after the numerals 2, 3, 4 (but not 12-14 or 112-114). + # The genitive plural (5 and above) lives in PluralGenitive below. + "akr": "akry", + "amper": "ampery", + "angstrem": "angstremy", + "atmosfera": "atmosfery", + "bajt": "bajty", + "bar": "bary", + "baryłka": "baryłki", + "bekerel": "bekerele", + "bel": "bele", + "bit": "bity", + "bitcoin": "bitcoiny", + "bod": "body", + "btu": "btu", + "buszel": "buszle", + "cal": "cale", + "cal kwadratowy": "cale kwadratowe", + "cal sześcienny": "cale sześcienne", + "cent": "centy", + "cetnar": "cetnary", + "colon": "colony", + "dalton": "daltony", + "decybel": "decybele", + "dolar": "dolary", + "drachma": "drachmy", + "drachma płynu": "drachmy płynu", + "dyna": "dyny", + "dzień": "dni", + "dżul": "dżule", + "eksbibajt": "eksbibajty", + "eksbibit": "eksbibity", + "elektronowolt": "elektronowolty", + "erg": "ergi", + "euro": "euro", + "farad": "farady", + "fermi": "fermi", + "funt": "funty", + "funt trojański": "funty trojańskie", + "galon": "galony", + "gibibajt": "gibibajty", + "gibibit": "gibibity", + "gill": "gille", + "godzina": "godziny", + "grad": "grady", + "gram": "gramy", + "gran": "grany", + "grej": "greje", + "hektar": "hektary", + "henr": "henry", + "herc": "herce", + "jard kwadratowy": "jardy kwadratowe", + "jard sześcienny": "jardy sześcienne", + "jednostka astronomiczna": "jednostki astronomiczne", + "jednostka masy atomowej": "jednostki masy atomowej", + "jobibajt": "jobibajty", + "jobibit": "jobibity", + "kaloria": "kalorie", + "kandela": "kandele", + "katal": "katale", + "kelwin": "kelwiny", + "kibibajt": "kibibajty", + "kibibit": "kibibity", + "kiur": "kiury", + "koń mechaniczny": "konie mechaniczne", + "kubek": "kubki", + "kulomb": "kulomby", + "kwarta": "kwarty", + "lir": "liry", + "litr": "litry", + "luks": "luksy", + "lumen": "lumeny", + "mebibajt": "mebibajty", + "mebibit": "mebibity", + "metr": "metry", + "mho": "mho", + "mila": "mile", + "mila kwadratowa": "mile kwadratowe", + "mila na galon": "mile na galon", + "mila na godzinę": "mile na godzinę", + "minuta": "minuty", + "minuta łuku": "minuty łuku", + "mol": "mole", + "molowy": "molowe", + "neper": "nepery", + "niuton": "niutony", + "obrót na minutę": "obroty na minutę", + "ogniwo": "ogniwa", + "om": "omy", + "parsek": "parseki", + "paskal": "paskale", + "pebibajt": "pebibajty", + "pebibit": "pebibity", + "pek": "peki", + "pennyweight": "pennyweight", + "peso": "peso", + "pinta": "pinty", + "pręt": "pręty", + "radian": "radiany", + "rentgen": "rentgeny", + "rok": "lata", + "rok świetlny": "lata świetlne", + "rupia": "rupie", + "sekunda": "sekundy", + "sekunda łuku": "sekundy łuku", + "siemens": "siemensy", + "siwert": "siwerty", + "steradian": "steradiany", + "stopa": "stopy", "stopa deskowa": "stopy deskowe", + "stopa kwadratowa": "stopy kwadratowe", + "stopa sześcienna": "stopy sześcienne", "stopień": "stopnie", "stopień Celsjusza": "stopnie Celsjusza", "stopień Fahrenheita": "stopnie Fahrenheita", - "degree celsius": "stopnie Celsjusza", - "degree fahrenheit": "stopnie Fahrenheita", - "henr": "henry", - "wolt": "wolty", + "szekel": "szekle", + "tebibajt": "tebibajty", + "tebibit": "tebibity", + "tesla": "tesle", + "tona": "tony", + "tydzień": "tygodnie", + "uncja": "uncje", + "uncja płynu": "uncje płynu", + "uncja trojańska": "uncje trojańskie", "wat": "waty", - "hertz": "herce", - "luks": "luksy", - "siemens": "siemens", - "obrót na minutę": "obroty na minutę", - "mila na godzinę": "mile na godzinę", - "mila na galon": "mile na galon", - "colon": "colony", "lir": "liry", "won": "wony", "bitcoin": "bitcoiny" + "weber": "webery", + "wolt": "wolty", + "won": "wony", + "zebibajt": "zebibajty", + "zebibit": "zebibity", + "łańcuch": "łańcuchy", + "łyżeczka": "łyżeczki", + "łyżka stołowa": "łyżki stołowe" + } + +- PluralGenitive: { + # Genitive plural - after 5 and above, and after 12-14, 112-114. + # Polish inflects the noun according to the numeral, so a single plural + # form is not enough: 2 metry but 5 metrów. The 'unit' and 'currency' + # rules pick the section based on the value of the numeral. + "akr": "akrów", + "amper": "amperów", + "angstrem": "angstremów", + "atmosfera": "atmosfer", + "bajt": "bajtów", + "bar": "barów", + "baryłka": "baryłek", + "bekerel": "bekereli", + "bel": "beli", + "bit": "bitów", + "bitcoin": "bitcoinów", + "bod": "bodów", + "btu": "btu", + "buszel": "buszli", + "cal": "cali", + "cal kwadratowy": "cali kwadratowych", + "cal sześcienny": "cali sześciennych", + "cent": "centów", + "cetnar": "cetnarów", + "colon": "colonów", + "dalton": "daltonów", + "decybel": "decybeli", + "dolar": "dolarów", + "drachma": "drachm", + "drachma płynu": "drachm płynu", + "dyna": "dyn", + "dzień": "dni", + "dżul": "dżuli", + "eksbibajt": "eksbibajtów", + "eksbibit": "eksbibitów", + "elektronowolt": "elektronowoltów", + "erg": "ergów", + "euro": "euro", + "farad": "faradów", + "fermi": "fermi", + "funt": "funtów", + "funt trojański": "funtów trojańskich", + "galon": "galonów", + "gibibajt": "gibibajtów", + "gibibit": "gibibitów", + "gill": "gilli", + "godzina": "godzin", + "grad": "gradów", + "gram": "gramów", + "gran": "granów", + "grej": "grejów", + "hektar": "hektarów", + "henr": "henrów", + "herc": "herców", + "jard kwadratowy": "jardów kwadratowych", + "jard sześcienny": "jardów sześciennych", + "jednostka astronomiczna": "jednostek astronomicznych", + "jednostka masy atomowej": "jednostek masy atomowej", + "jobibajt": "jobibajtów", + "jobibit": "jobibitów", + "kaloria": "kalorii", + "kandela": "kandeli", + "katal": "katali", + "kelwin": "kelwinów", + "kibibajt": "kibibajtów", + "kibibit": "kibibitów", + "kiur": "kiurów", + "koń mechaniczny": "koni mechanicznych", + "kubek": "kubków", + "kulomb": "kulombów", + "kwarta": "kwart", + "lir": "lirów", + "litr": "litrów", + "luks": "luksów", + "lumen": "lumenów", + "mebibajt": "mebibajtów", + "mebibit": "mebibitów", + "metr": "metrów", + "mho": "mho", + "mila": "mil", + "mila kwadratowa": "mil kwadratowych", + "mila na galon": "mil na galon", + "mila na godzinę": "mil na godzinę", + "minuta": "minut", + "minuta łuku": "minut łuku", + "mol": "moli", + "molowy": "molowych", + "neper": "neperów", + "niuton": "niutonów", + "obrót na minutę": "obrotów na minutę", + "ogniwo": "ogniw", + "om": "omów", + "parsek": "parseków", + "paskal": "paskali", + "pebibajt": "pebibajtów", + "pebibit": "pebibitów", + "pek": "peków", + "pennyweight": "pennyweight", + "peso": "peso", + "pinta": "pint", + "pręt": "prętów", + "radian": "radianów", + "rentgen": "rentgenów", + "rok": "lat", + "rok świetlny": "lat świetlnych", + "rupia": "rupii", + "sekunda": "sekund", + "sekunda łuku": "sekund łuku", + "siemens": "siemensów", + "siwert": "siwertów", + "steradian": "steradianów", + "stopa": "stóp", + "stopa deskowa": "stóp deskowych", + "stopa kwadratowa": "stóp kwadratowych", + "stopa sześcienna": "stóp sześciennych", + "stopień": "stopni", + "stopień Celsjusza": "stopni Celsjusza", + "stopień Fahrenheita": "stopni Fahrenheita", + "szekel": "szekli", + "tebibajt": "tebibajtów", + "tebibit": "tebibitów", + "tesla": "tesli", + "tona": "ton", + "tydzień": "tygodni", + "uncja": "uncji", + "uncja płynu": "uncji płynu", + "uncja trojańska": "uncji trojańskich", + "wat": "watów", + "weber": "weberów", + "wolt": "woltów", + "won": "wonów", + "zebibajt": "zebibajtów", + "zebibit": "zebibitów", + "łańcuch": "łańcuchów", + "łyżeczka": "łyżeczek", + "łyżka stołowa": "łyżek stołowych" } # Lines starting with "#" are a comment diff --git a/Rules/Languages/pl/navigate.yaml b/Rules/Languages/pl/navigate.yaml index c99b60490..bb89c8a9a 100644 --- a/Rules/Languages/pl/navigate.yaml +++ b/Rules/Languages/pl/navigate.yaml @@ -48,9 +48,9 @@ - if: "$MatchCounter = 0 and $SayCommand = 'true'" then_test: - if: "self::m:math and starts-with($NavCommand, 'ZoomOut')" - then: [t: "maksymalnie oddalono", pause: "medium"] + then: [T: "maksymalnie oddalono", pause: "medium"] - else_if: "IsNode(., 'leaf') and starts-with($NavCommand, 'ZoomIn')" - then: [t: "maksymalnie oddalono", pause: "medium"] + then: [T: "maksymalnie oddalono", pause: "medium"] else: - test: # CommandOffset to 1 + długość ANGIELSKIEGO rdzenia $NavCommand @@ -133,13 +133,13 @@ replace: - test: - if: "$Move2D = 'in'" - then: [t: "do"] # phrase(move 'in' to a part) + then: [T: "do"] # phrase(move 'in' to a part) - else_if: "$Move2D = 'out of'" - then: [t: "z"] # phrase(move 'out of' a part) + then: [T: "z"] # phrase(move 'out of' a part) - else_if: "$Move2D = 'end of'" - then: [t: "na koniec"] # phrase(move to the 'end of' a part) + then: [T: "na koniec"] # phrase(move to the 'end of' a part) - else_if: "$Move2D = 'start of'" - then: [t: "na początek"] # phrase(move to the 'start of' a part) + then: [T: "na początek"] # phrase(move to the 'start of' a part) else: [x: "$Move2D"] - test: - if: "$NumPrecedingSiblings=0" @@ -148,8 +148,8 @@ then: - test: # in postscripts -- base shifts by one if: "$NumPrecedingSiblings mod 2 = 0" - then: [t: "lewego indeksu dolnego"] # phrase(x with 'subscript' 2) - else: [t: "lewego indeksu górnego"] # phrase(x with 'superscript' 2) + then: [T: "lewego indeksu dolnego"] # phrase(x with 'subscript' 2) + else: [T: "lewego indeksu górnego"] # phrase(x with 'superscript' 2) else: - test: if: "$NumPrecedingSiblings mod 2 = 0" @@ -171,15 +171,15 @@ replace: - test: - if: "$Move2D = 'in'" - then: [t: "do"] # phrase(move 'in' to a part) + then: [T: "do"] # phrase(move 'in' to a part) - else_if: "$Move2D = 'out of' and starts-with($PartName, 'st')" then: [t: "ze"] # "ze stopnia" (eufonia przed 'st') - else_if: "$Move2D = 'out of'" - then: [t: "z"] # phrase(move 'out of' a part) + then: [T: "z"] # phrase(move 'out of' a part) - else_if: "$Move2D = 'end of'" - then: [t: "na koniec"] # phrase(move to the 'end of' a part) + then: [T: "na koniec"] # phrase(move to the 'end of' a part) - else_if: "$Move2D = 'start of'" - then: [t: "na początek"] # phrase(move to the 'start of' a part) + then: [T: "na początek"] # phrase(move to the 'start of' a part) else: [x: "$Move2D"] - test: - if: "$PartName != ''" @@ -203,7 +203,7 @@ then: [x: "SpeakIntentName(name(.), $Verbosity, 'silent')"] - else: [x: "SpeakIntentName(name(.), $Verbosity, 'other')"] else: - - t: "części" # phrase(the 'part' of the expression) + - T: "części" # phrase(the 'part' of the expression) - x: "count($Child2D/preceding-sibling::*) + 1" - pause: "medium" @@ -226,19 +226,19 @@ then: - test: - if: "$PreviousNavCommand = 'ZoomIn'" - then: [t: "cofnij przybliżenie"] # phrase('undo zoom in') + then: [T: "cofnij przybliżenie"] # phrase('undo zoom in') - else_if: "$PreviousNavCommand = 'ZoomOut'" - then: [t: "cofnij oddalenie"] # phrase('undo zoom out') + then: [T: "cofnij oddalenie"] # phrase('undo zoom out') - else_if: "$PreviousNavCommand = 'ZoomInAll'" - then: [t: "cofnij maksymalne przybliżenie"] # phrase('undo zooming in all the way') + then: [T: "cofnij maksymalne przybliżenie"] # phrase('undo zooming in all the way') - else_if: "$PreviousNavCommand = 'ZoomOutAll'" - then: [t: "cofnij maksymalne oddalenie"] # phrase('undo zooming out all the way') + then: [T: "cofnij maksymalne oddalenie"] # phrase('undo zooming out all the way') - else_if: "$PreviousNavCommand = 'MovePrevious' or $PreviousNavCommand = 'MovePreviousZoom'" - then: [t: "cofnij ruch w lewo"] # phrase('undo move left') + then: [T: "cofnij ruch w lewo"] # phrase('undo move left') - else_if: "$PreviousNavCommand = 'MoveNext' or $PreviousNavCommand = 'MoveNextZoom'" - then: [t: "cofnij ruch w prawo"] # phrase('undo move right') + then: [T: "cofnij ruch w prawo"] # phrase('undo move right') - else_if: "$PreviousNavCommand = 'None'" - then: [t: "brak poprzedniego polecenia"] # phrase('no previous command') + then: [T: "brak poprzedniego polecenia"] # phrase('no previous command') - pause: "medium" - set_variables: [NavNode: "@id"] @@ -261,7 +261,7 @@ replace: - test: if: "$MatchCounter = 0 and $NavVerbosity != 'Terse'" - then: [t: "maksymalnie przybliżono", pause: "long"] # phrase('zoomed in all of the way') + then: [T: "maksymalnie przybliżono", pause: "long"] # phrase('zoomed in all of the way') - test: if: "$ReadZoomLevel!=-1" then: @@ -552,12 +552,12 @@ then: - test: - if: "$NavCommand = 'MoveStart'" - then: [t: "przejdź na początek wyrażenia matematycznego"] # phrase('move to start of math') + then: [T: "przejdź na początek wyrażenia matematycznego"] # phrase('move to start of math') - else_if: "$NavCommand = 'MoveLineStart'" - then: [t: "przejdź na początek linii"] # phrase('move to start of line') + then: [T: "przejdź na początek linii"] # phrase('move to start of line') - else_if: "$NavCommand = 'MoveEnd'" - then: [t: "przejdź na koniec wyrażenia matematycznego"] # phrase('move to end of math') - else: [t: "przejdź na koniec linii"] # "$NavCommand = 'MoveLineEnd'" # phrase('move to end of line') + then: [T: "przejdź na koniec wyrażenia matematycznego"] # phrase('move to end of math') + else: [T: "przejdź na koniec linii"] # "$NavCommand = 'MoveLineEnd'" # phrase('move to end of line') - pause: "medium" - test: if: "$NavCommand = 'MoveStart' or $NavCommand = 'MoveLineStart'" @@ -597,8 +597,8 @@ then: - test: if: "$NavCommand = 'MoveLineStart'" - then: [t: "przejdź na początek linii"] # phrase('move to start of line') - else: [t: "przejdź na koniec linii"] # "$NavCommand = 'MoveLineEnd'" # phrase('move to end of line') + then: [T: "przejdź na początek linii"] # phrase('move to start of line') + else: [T: "przejdź na koniec linii"] # "$NavCommand = 'MoveLineEnd'" # phrase('move to end of line') - pause: "medium" - test: if: "self::m:mrow or @data-from-mathml = 'mrow'" @@ -634,7 +634,7 @@ - "$NavCommand='MoveColumnStart' or $NavCommand='MoveColumnEnd' or" - "$NavCommand='ReadCellCurrent'" replace: - - t: "poza tabelą" # phrase('not in table') + - T: "poza tabelą" # phrase('not in table') - pause: long - set_variables: [SpeakExpression: "'false'"] @@ -648,7 +648,7 @@ - test: if: "$NavVerbosity = 'Verbose'" then: - - t: "przejdź w lewo" # phrase('move left') + - T: "przejdź w lewo" # phrase('move left') - pause: short - test: if: "$NavVerbosity != 'Terse'" @@ -665,7 +665,7 @@ else: - set_variables: [NavNode: "preceding-sibling::*[1]/*[1]/@id"] else: - - t: "brak poprzedniej kolumny" # phrase('no previous column' in the table) + - T: "brak poprzedniej kolumny" # phrase('no previous column' in the table) - set_variables: [SpeakExpression: "'false'"] - name: move-cell-next @@ -678,7 +678,7 @@ - test: if: "$NavVerbosity = 'Verbose'" then: - - t: "przejdź w prawo" # phrase('move right') + - T: "przejdź w prawo" # phrase('move right') - pause: short - test: if: "$NavVerbosity != 'Terse'" @@ -695,7 +695,7 @@ else: - set_variables: [NavNode: "following-sibling::*[1]/*[1]/@id"] else: - - t: "brak następnej kolumny" # phrase('no next column' in the table) + - T: "brak następnej kolumny" # phrase('no next column' in the table) - set_variables: [SpeakExpression: "'false'"] - name: move-cell-up @@ -711,7 +711,7 @@ - test: if: "$NavVerbosity = 'Verbose'" then: - - t: "przejdź w górę" # phrase('move up' to previous row in the table) + - T: "przejdź w górę" # phrase('move up' to previous row in the table) - pause: short - test: if: "$NavVerbosity != 'Terse'" @@ -731,7 +731,7 @@ else: - set_variables: [NavNode: "../preceding-sibling::*[1]/*[$Column]/*[1]/@id"] else: - - t: "brak poprzedniego wiersza" # phrase('no previous row' in the table) + - T: "brak poprzedniego wiersza" # phrase('no previous row' in the table) - set_variables: [SpeakExpression: "'false'"] - name: move-cell-down @@ -747,7 +747,7 @@ - test: if: "$NavVerbosity = 'Verbose'" then: - - t: "przejdź w dół" # phrase('move down to the next row in the table) + - T: "przejdź w dół" # phrase('move down to the next row in the table) - pause: short - test: if: "$NavVerbosity != 'Terse'" @@ -767,7 +767,7 @@ else: - set_variables: [NavNode: "../following-sibling::*[1]/*[$Column]/*[1]/@id"] else: - - t: "brak następnego wiersza" # phrase('no next row' in the table) + - T: "brak następnego wiersza" # phrase('no next row' in the table) - set_variables: [SpeakExpression: "'false'"] - name: move-cell-up @@ -777,7 +777,7 @@ - test: if: "$NavVerbosity = 'Verbose'" then: - - t: "przejdź do poprzedniego wiersza" # phrase('move to previous row' to the previous row in the table) + - T: "przejdź do poprzedniego wiersza" # phrase('move to previous row' to the previous row in the table) - pause: medium - test: if: "preceding-sibling::*" @@ -791,7 +791,7 @@ else: - set_variables: [NavNode: "preceding-sibling::*[1]/@id"] else: - - t: "brak poprzedniego wiersza" # phrase('no previous row' in the table) + - T: "brak poprzedniego wiersza" # phrase('no previous row' in the table) - set_variables: [SpeakExpression: "'false'"] - name: move-cell-down @@ -801,7 +801,7 @@ - test: if: "$NavVerbosity = 'Verbose'" then: - - t: "przejdź do następnego wiersza" # phrase('move to next row' to the next row in the table) + - T: "przejdź do następnego wiersza" # phrase('move to next row' to the next row in the table) - pause: medium - test: if: "following-sibling::*" @@ -815,7 +815,7 @@ else: - set_variables: [NavNode: "following-sibling::*[1]/@id"] else: - - t: "brak następnego wiersza" # phrase('no next row' in the table) + - T: "brak następnego wiersza" # phrase('no next row' in the table) - set_variables: [SpeakExpression: "'false'"] - name: move-cell-previous @@ -826,9 +826,9 @@ - test: if: "$NavVerbosity = 'Verbose'" then: - - t: "przejdź do poprzedniej kolumny" # phrase('move to previous column' to the previous row in the table) + - T: "przejdź do poprzedniej kolumny" # phrase('move to previous column' to the previous row in the table) - pause: medium - - t: "brak poprzedniej kolumny" # phrase('no previous column' in the table) + - T: "brak poprzedniej kolumny" # phrase('no previous column' in the table) - set_variables: [SpeakExpression: "'false'"] - name: move-cell-next @@ -839,9 +839,9 @@ - test: if: "$NavVerbosity = 'Verbose'" then: - - t: "przejdź do następnej kolumny" # phrase('move up' to the previous row in the table) + - T: "przejdź do następnej kolumny" # phrase('move up' to the previous row in the table) - pause: medium - - t: "brak następnej kolumny" # phrase('no next row' in the table) + - T: "brak następnej kolumny" # phrase('no next row' in the table) - set_variables: [SpeakExpression: "'false'"] - name: default-read-cell @@ -857,7 +857,7 @@ - test: if: "$NavVerbosity = 'Verbose'" then: - - t: "czytaj bieżący element" # phrase('read current entry' in the table) + - T: "czytaj bieżący element" # phrase('read current entry' in the table) - pause: medium - test: if: "$NavVerbosity != 'Terse'" @@ -870,7 +870,7 @@ - pause: short - set_variables: [NavNode: "$MTD[1]/*[1]/@id"] else: - - t: "poza tabelą" # phrase('not in table' or matrix) + - T: "poza tabelą" # phrase('not in table' or matrix) - pause: long - set_variables: [SpeakExpression: "'false'"] @@ -1110,7 +1110,7 @@ then: - x: "ancestor::m:mtd[1]" # try again on an mtd node else: - - t: "poza tabelą" # phrase('not in table' or matrix) + - T: "poza tabelą" # phrase('not in table' or matrix) - pause: long - set_variables: [SpeakExpression: "'false'"] @@ -1187,7 +1187,7 @@ - test: if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" then: - - t: "nie można przejść w prawo" # phrase('cannot move right') + - T: "nie można przejść w prawo" # phrase('cannot move right') - pause: medium - with: variables: @@ -1208,14 +1208,14 @@ - test: if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" then: - - t: "nie można" # phrase('cannot' move right in expression) + - T: "nie można" # phrase('cannot' move right in expression) - test: - if: "$NavCommand = 'MoveNext'" - then: [t: "przejść"] # phrase('move' to next entry in table) + then: [T: "przejść"] # phrase('move' to next entry in table) - else_if: "$NavCommand = 'ReadNext'" - then: [t: "odczytać"] # phrase('read' next entry in table) - else: [t: "opisać"] # phrase('describe' next entry in table) - - t: "w prawo" # phrase(move 'right') + then: [T: "odczytać"] # phrase('read' next entry in table) + else: [T: "opisać"] # phrase('describe' next entry in table) + - T: "w prawo" # phrase(move 'right') - pause: short - T: "koniec matematyki" # phrase(move 'end of math') - pause: long @@ -1382,7 +1382,7 @@ - test: if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose' and $NavCommand = 'MovePrevious'" then: - - t: "nie można przejść w lewo" # phrase('cannot move left' in expression) + - T: "nie można przejść w lewo" # phrase('cannot move left' in expression) - pause: medium - with: variables: @@ -1399,7 +1399,7 @@ - "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and" - "(self::m:math or name(EdgeNode(., 'left', 'math'))='math')" replace: - - t: "początek wyrażenia matematycznego" # phrase('start of math') + - T: "początek wyrażenia matematycznego" # phrase('start of math') - pause: long - set_variables: [SpeakExpression: "'false'"] @@ -1412,7 +1412,7 @@ - test: if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" then: - - t: "nie można przejść w lewo" # phrase('cannot move left') + - T: "nie można przejść w lewo" # phrase('cannot move left') - pause: short - with: variables: [Move2D: "'start of'", Child2D: "."] @@ -1573,16 +1573,16 @@ - test: - if: "$NavMode = 'Enhanced'" then: - - t: "znak" # phrase(a mathematical 'character') + - T: "znak" # phrase(a mathematical 'character') - set_variables: [NavMode: "'Character'", ReadZoomLevel: "1"] - else_if: "$NavMode = 'Character'" then: - - t: "prosty" # phrase(a 'simple' way to do something) + - T: "prosty" # phrase(a 'simple' way to do something) - set_variables: [NavMode: "'Simple'", ReadZoomLevel: "1"] - else: - - t: "rozszerzony" # phrase(an 'enhanced' way to do something) + - T: "rozszerzony" # phrase(an 'enhanced' way to do something) - set_variables: [NavMode: "'Enhanced'", ReadZoomLevel: "-1"] - - t: "tryb" # phrase(a simple 'mode' of use) + - T: "tryb" # phrase(a simple 'mode' of use) - pause: long - test: - if: "$NavMode != 'Enhanced'" # potentially need to zoom to the sibling @@ -1598,16 +1598,16 @@ - test: - if: "$NavMode = 'Enhanced'" then: - - t: "prosty" # phrase(an 'simple' way to do something) + - T: "prosty" # phrase(an 'simple' way to do something) - set_variables: [NavMode: "'Simple'", ReadZoomLevel: "1"] - else_if: "$NavMode = 'Character'" then: - - t: "rozszerzony" # phrase(an 'enhanced' way to do something) + - T: "rozszerzony" # phrase(an 'enhanced' way to do something) - set_variables: [NavMode: "'Enhanced'", ReadZoomLevel: "-1"] - else: - - t: "znak" # phrase(a mathematical 'character') + - T: "znak" # phrase(a mathematical 'character') - set_variables: [NavMode: "'Character'", ReadZoomLevel: "1"] - - t: "tryb" # phrase(a simple 'mode' of use) + - T: "tryb" # phrase(a simple 'mode' of use) - pause: long - test: - if: "$NavMode != 'Enhanced'" # potentially need to zoom to the sibling @@ -1623,7 +1623,7 @@ - test: if: "$Overview = 'true'" then: - - t: "czytaj wyrażenie po ruchu" # phrase('speak expression after move') + - T: "czytaj wyrażenie po ruchu" # phrase('speak expression after move') - pause: long - set_variables: [Overview: "'false'"] else: @@ -1640,9 +1640,9 @@ then: - test: - if: "$NavCommand = 'ReadCurrent'" - then: [t: "czytaj"] # phrase('read' next entry in table) - else: [t: "opisz"] # phrase('describe' next entry in table) - - t: "bieżący" # phrase('current' entry in table) + then: [T: "czytaj"] # phrase('read' next entry in table) + else: [T: "opisz"] # phrase('describe' next entry in table) + - T: "bieżący" # phrase('current' entry in table) - pause: long - set_variables: [NavNode: "@id"] @@ -1660,13 +1660,13 @@ then: - test: - if: "starts-with($NavCommand, 'Read')" - then: [t: "czytaj"] # phrase('read' next entry in table) + then: [T: "czytaj"] # phrase('read' next entry in table) - else_if: "starts-with($NavCommand, 'Describe')" - then: [t: "opisz"] # phrase('describe' next entry in table) + then: [T: "opisz"] # phrase('describe' next entry in table) - else_if: "starts-with($NavCommand, 'MoveTo')" - then: [t: "przejdź do"] # phrase('move to' the next entry in table) + then: [T: "przejdź do"] # phrase('move to' the next entry in table) else: [T: "zbiór"] # phrase('set' the value of the next entry in table) - - t: "symbol zastępczy" # phrase('placeholder' for the value) + - T: "symbol zastępczy" # phrase('placeholder' for the value) - x: "$PlaceMarkerIndex" - pause: long - set_variables: [NavNode: "$PlaceMarker"] @@ -1678,7 +1678,7 @@ - test: if: "$NavVerbosity != 'Terse'" then: - - t: "ustaw symbol zastępczy" # phrase('set placeholder' to the value) + - T: "ustaw symbol zastępczy" # phrase('set placeholder' to the value) - x: "$PlaceMarkerIndex" - pause: long - set_variables: [NavNode: "@id"] @@ -1710,11 +1710,11 @@ - test: if: "$NavCommand = 'WhereAmI'" then: - - t: "wewnątrz niczego więcej" # phrase('inside of nothing more') + - T: "wewnątrz niczego więcej" # phrase('inside of nothing more') - pause: long - set_variables: [SpeakExpression: "'false'"] else: - - t: "wewnątrz" # phrase('inside' a big expression) + - T: "wewnątrz" # phrase('inside' a big expression) - pause: medium - set_variables: [NavNode: "@id"] @@ -1722,7 +1722,7 @@ tag: "*" match: "$NavCommand = 'WhereAmI' or $NavCommand = 'WhereAmIAll'" replace: - - t: "wewnątrz" # phrase('inside' a big expression) + - T: "wewnątrz" # phrase('inside' a big expression) - pause: medium - test: - if: "$NavMode='Enhanced' and parent::*[self::m:mrow and (IsBracketed(., '(', ')', false) or IsBracketed(., '[', ']', false))]" diff --git a/Rules/Languages/pl/overview.yaml b/Rules/Languages/pl/overview.yaml index a65395889..a51169bb3 100644 --- a/Rules/Languages/pl/overview.yaml +++ b/Rules/Languages/pl/overview.yaml @@ -21,22 +21,22 @@ if: "IsNode(*[1], 'simple') and IsNode(*[2], 'simple')" then: - x: "*[1]" - - t: "przez" + - T: "przez" - x: "*[2]" else: - - t: "ułamek" + - T: "ułamek" - name: overview-default tag: [msqrt, "square-root"] match: "." replace: - - t: "pierwiastek kwadratowy" + - T: "pierwiastek kwadratowy" - test: if: "IsNode(*[1], 'simple')" then: - test: if: "$Verbosity!='Terse'" - then: [t: "z"] + then: [T: "z"] - x: "*[1]" - name: overview-default @@ -47,24 +47,24 @@ if: "*[2][self::m:mn]" then_test: - if: "*[2][.='2']" - then: [t: "pierwiastek kwadratowy"] + then: [T: "pierwiastek kwadratowy"] - else_if: "*[2][.='3']" - then: [t: "pierwiastek sześcienny"] + then: [T: "pierwiastek sześcienny"] - else_if: "*[2][not(contains(., '.'))]" - then: [x: "ToOrdinal(*[2])", t: "pierwiastek"] + then: [x: "ToOrdinal(*[2])", T: "pierwiastek"] else: - test: if: "*[2][self::m:mi][string-length(.)=1]" then: - x: "*[2]" else: {x: "*[2]"} - - t: "pierwiastek" + - T: "pierwiastek" - test: if: "IsNode(*[1], 'simple')" then: - test: if: "$Verbosity!='Terse'" - then: [t: "z"] + then: [T: "z"] - x: "*[1]" - name: matrix-override @@ -75,12 +75,12 @@ replace: - t: "" - x: count(*[2]/*) - - t: "na" + - T: "na" - 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: "wyznacznik"] - else: [t: "macierz"] + then: [T: "wyznacznik"] + else: [T: "macierz"] - name: overview-default tag: mtable @@ -88,9 +88,9 @@ replace: - t: "" - x: count(*[2]/*) - - t: "na" + - T: "na" - x: count(*[2]/*[self::m:mtr][1]/*) - - t: "tabela" + - T: "tabela" - name: short-mrow tag: mrow @@ -114,6 +114,6 @@ - pause: auto - x: "*[5]" - pause: auto - - t: "i tak dalej" + - T: "i tak dalej" - include: "SimpleSpeak_Rules.yaml" diff --git a/Rules/Languages/pl/unicode-full.yaml b/Rules/Languages/pl/unicode-full.yaml index 8246f4650..3c378bc8c 100644 --- a/Rules/Languages/pl/unicode-full.yaml +++ b/Rules/Languages/pl/unicode-full.yaml @@ -1,28 +1,28 @@ --- - - "¢": [t: "centy"] # 0xa2 - - "£": [t: "funty"] # 0xa3 - - "¤": [t: "znak waluty"] # 0xa4 - - "¥": [t: "jen"] # 0xa5 - - "¦": [t: "przerwana kreska"] # 0xa6 - - "§": [t: "paragraf"] # 0xa7 - - "¨": [t: "diereza"] # 0xa8 - - "©": [t: "znak praw autorskich"] # 0xa9 - - "ª": [t: "żeński wskaźnik liczebnika porządkowego"] # 0xaa + - "¢": [T: "centy"] # 0xa2 + - "£": [T: "funty"] # 0xa3 + - "¤": [T: "znak waluty"] # 0xa4 + - "¥": [T: "jen"] # 0xa5 + - "¦": [T: "przerwana kreska"] # 0xa6 + - "§": [T: "paragraf"] # 0xa7 + - "¨": [T: "diereza"] # 0xa8 + - "©": [T: "znak praw autorskich"] # 0xa9 + - "ª": [T: "żeński wskaźnik liczebnika porządkowego"] # 0xaa - "¬": [T: "nie"] # 0xac - - "«": [t: "lewy podwójny cudzysłów ostrokątny"] # 0xab + - "«": [T: "lewy podwójny cudzysłów ostrokątny"] # 0xab - "¯": # 0xaf - test: if: "ancestor::m:modified-variable and preceding-sibling::*[1][self::m:mi]" - then: [t: "kreska"] - else: [t: "linia"] - - "²": [t: "dwa"] # 0xb2 - - "³": [t: "trzy"] # 0xb3 - - "´": [t: "akut"] # 0xb4 - - "µ": [t: "mikro"] # 0xb5 - - "¹": [t: "jeden"] # 0xb9 - - "º": [t: "męski wskaźnik liczebnika porządkowego"] # 0xb9 + then: [T: "kreska"] + else: [T: "linia"] + - "²": [T: "dwa"] # 0xb2 + - "³": [T: "trzy"] # 0xb3 + - "´": [T: "akut"] # 0xb4 + - "µ": [T: "mikro"] # 0xb5 + - "¹": [T: "jeden"] # 0xb9 + - "º": [T: "męski wskaźnik liczebnika porządkowego"] # 0xb9 - "·": - test: if: "$SpeechStyle != 'ClearSpeak' or $ClearSpeak_MultSymbolDot = 'Auto'" @@ -35,162 +35,162 @@ else_test: if: $ClearSpeak_MultSymbolX = 'By' then: [T: "na"] - else: [t: "krzyż"] + else: [T: "krzyż"] - "÷": [T: "podzielone przez"] # 0xf7 - - "¡": [t: "odwrócony wykrzyknik"] # 0xa1 - - "¶": [t: "znak akapitu"] # 0xb6 - - "¿": [t: "odwrócony znak zapytania"] # 0xbf - - - "ʰ": [t: "modyfikator mały h"] # 0x2b0 - - "ʱ": [t: "modyfikator mały h z haczykiem"] # 0x2b1 - - "ʲ": [t: "modyfikator mały j"] # 0x2b2 - - "ʳ": [t: "modyfikator mały R"] # 0x2b3 - - "ʴ": [t: "modyfikator mały odwrócony R"] # 0x2b4 - - "ʵ": [t: "modyfikator mały odwrócony R z haczykiem"] # 0x2b5 + - "¡": [T: "odwrócony wykrzyknik"] # 0xa1 + - "¶": [T: "znak akapitu"] # 0xb6 + - "¿": [T: "odwrócony znak zapytania"] # 0xbf + + - "ʰ": [T: "modyfikator mały h"] # 0x2b0 + - "ʱ": [T: "modyfikator mały h z haczykiem"] # 0x2b1 + - "ʲ": [T: "modyfikator mały j"] # 0x2b2 + - "ʳ": [T: "modyfikator mały R"] # 0x2b3 + - "ʴ": [T: "modyfikator mały odwrócony R"] # 0x2b4 + - "ʵ": [T: "modyfikator mały odwrócony R z haczykiem"] # 0x2b5 - "ʶ": # 0x2b6 - - t: "modyfikator mały odwrócony" + - T: "modyfikator mały odwrócony" - spell: "translate('R', 'R', 'R')" - - "ʷ": [t: "modyfikator mały w"] # 0x2b7 - - "ʸ": [t: "modyfikator mały y"] # 0x2b8 - - "ʹ": [t: "modyfikator prim"] # 0x2b9 - - "ʺ": [t: "modyfikator podwójny prim"] # 0x2ba - - "ʻ": [t: "modyfikator odwrócony przecinek"] # 0x2bb - - "ʼ": [t: "modyfikator apostrof"] # 0x2bc - - "ʽ": [t: "modyfikator odwrócony przecinek"] # 0x2bd - - "ʾ": [t: "modyfikator prawa połowa pierścień"] # 0x2be - - "ʿ": [t: "modyfikator lewa połowa pierścień"] # 0x2bf - - "ˀ": [t: "modyfikator zwarcie krtaniowe"] # 0x2c0 - - "ˁ": [t: "modyfikator odwrócone zwarcie krtaniowe"] # 0x2c1 - - "˂": [t: "modyfikator lewy grot strzałki"] # 0x2c2 - - "˃": [t: "modyfikator prawy grot strzałki"] # 0x2c3 - - "˄": [t: "modyfikator górny grot strzałki"] # 0x2c4 - - "˅": [t: "modyfikator dolny grot strzałki"] # 0x2c5 - - "ˆ": [t: "modyfikator akcent daszkowy"] # 0x2c6 - - "ˇ": [t: "haczek"] # 0x2c7 - - "ˈ": [t: "modyfikator pionowej kreski"] # 0x2c8 - - "ˉ": [t: "modyfikator makron"] # 0x2c9 - - "ˊ": [t: "modyfikator akut akcent"] # 0x2ca - - "ˋ": [t: "modyfikator grawis akcent"] # 0x2cb - - "ˌ": [t: "modyfikator dolnej pionowej kreski"] # 0x2cc - - "ˍ": [t: "modyfikator dolny makron"] # 0x2cd - - "ˎ": [t: "modyfikator dolny akcent grawis"] # 0x2ce - - "ˏ": [t: "modyfikator dolny akcent akut"] # 0x2cf - - "ː": [t: "modyfikator trójkątny dwukropek"] # 0x2d0 - - "ˑ": [t: "modyfikator połowa trójkątny dwukropek"] # 0x2d1 - - "˒": [t: "modyfikator wyśrodkowany prawa połowa pierścień"] # 0x2d2 - - "˓": [t: "modyfikator wyśrodkowany lewa połowa pierścień"] # 0x2d3 - - "˔": [t: "modyfikator górnej pinezki"] # 0x2d4 - - "˕": [t: "modyfikator dolnej pinezki"] # 0x2d5 - - "˖": [t: "modyfikator znaku plus"] # 0x2d6 - - "˗": [t: "modyfikator znaku minus"] # 0x2d7 - - "˘": [t: "znak krótkości"] # 0x2d8 + - "ʷ": [T: "modyfikator mały w"] # 0x2b7 + - "ʸ": [T: "modyfikator mały y"] # 0x2b8 + - "ʹ": [T: "modyfikator prim"] # 0x2b9 + - "ʺ": [T: "modyfikator podwójny prim"] # 0x2ba + - "ʻ": [T: "modyfikator odwrócony przecinek"] # 0x2bb + - "ʼ": [T: "modyfikator apostrof"] # 0x2bc + - "ʽ": [T: "modyfikator odwrócony przecinek"] # 0x2bd + - "ʾ": [T: "modyfikator prawa połowa pierścień"] # 0x2be + - "ʿ": [T: "modyfikator lewa połowa pierścień"] # 0x2bf + - "ˀ": [T: "modyfikator zwarcie krtaniowe"] # 0x2c0 + - "ˁ": [T: "modyfikator odwrócone zwarcie krtaniowe"] # 0x2c1 + - "˂": [T: "modyfikator lewy grot strzałki"] # 0x2c2 + - "˃": [T: "modyfikator prawy grot strzałki"] # 0x2c3 + - "˄": [T: "modyfikator górny grot strzałki"] # 0x2c4 + - "˅": [T: "modyfikator dolny grot strzałki"] # 0x2c5 + - "ˆ": [T: "modyfikator akcent daszkowy"] # 0x2c6 + - "ˇ": [T: "haczek"] # 0x2c7 + - "ˈ": [T: "modyfikator pionowej kreski"] # 0x2c8 + - "ˉ": [T: "modyfikator makron"] # 0x2c9 + - "ˊ": [T: "modyfikator akut akcent"] # 0x2ca + - "ˋ": [T: "modyfikator grawis akcent"] # 0x2cb + - "ˌ": [T: "modyfikator dolnej pionowej kreski"] # 0x2cc + - "ˍ": [T: "modyfikator dolny makron"] # 0x2cd + - "ˎ": [T: "modyfikator dolny akcent grawis"] # 0x2ce + - "ˏ": [T: "modyfikator dolny akcent akut"] # 0x2cf + - "ː": [T: "modyfikator trójkątny dwukropek"] # 0x2d0 + - "ˑ": [T: "modyfikator połowa trójkątny dwukropek"] # 0x2d1 + - "˒": [T: "modyfikator wyśrodkowany prawa połowa pierścień"] # 0x2d2 + - "˓": [T: "modyfikator wyśrodkowany lewa połowa pierścień"] # 0x2d3 + - "˔": [T: "modyfikator górnej pinezki"] # 0x2d4 + - "˕": [T: "modyfikator dolnej pinezki"] # 0x2d5 + - "˖": [T: "modyfikator znaku plus"] # 0x2d6 + - "˗": [T: "modyfikator znaku minus"] # 0x2d7 + - "˘": [T: "znak krótkości"] # 0x2d8 - "˙": [T: "kropka"] # 0x2d9 - - "˚": [t: "pierścień nad"] # 0x2da + - "˚": [T: "pierścień nad"] # 0x2da - "˛": [t: "ogonek"] # 0x2db - - "˜": [t: "mały tylda"] # 0x2dc - - "˝": [t: "podwójny akut akcent"] # 0x2dd - - "˞": [t: "modyfikator rotycznego haczyka"] # 0x2de - - "˟": [t: "modyfikator krzyż akcent"] # 0x2df - - "ˠ": [t: "modyfikator mały gamma"] # 0x2e0 - - "ˡ": [t: "modyfikator mały L"] # 0x2e1 - - "ˢ": [t: "modyfikator mały s"] # 0x2e2 - - "ˣ": [t: "modyfikator mały X"] # 0x2e3 - - "ˤ": [t: "modyfikator mały odwrócone zwarcie krtaniowe"] # 0x2e4 - - "˥": [t: "modyfikator kreski bardzo wysokiego tonu"] # 0x2e5 - - "˦": [t: "modyfikator kreski wysokiego tonu"] # 0x2e6 - - "˧": [t: "modyfikator środkowy tonowy kreska"] # 0x2e7 - - "˨": [t: "modyfikator kreski niskiego tonu"] # 0x2e8 - - "˩": [t: "modyfikator kreski bardzo niskiego tonu"] # 0x2e9 - - "˪": [t: "modyfikator znak tonu yin odchodzącego"] # 0x2ea - - "˫": [t: "modyfikator znak tonu yang odchodzącego"] # 0x2eb - - "ˬ": [t: "modyfikator dźwięczność"] # 0x2ec - - "˭": [t: "modyfikator nieaspirowany"] # 0x2ed - - "ˮ": [t: "modyfikator podwójny apostrof"] # 0x2ee - - "˯": [t: "modyfikator dolny grot strzałki"] # 0x2ef - - "˰": [t: "modyfikator dolny górny grot strzałki"] # 0x2f0 - - "˱": [t: "modyfikator dolny lewy grot strzałki"] # 0x2f1 - - "˲": [t: "modyfikator dolny prawy grot strzałki"] # 0x2f2 - - "˳": [t: "modyfikator dolny pierścień"] # 0x2f3 - - "˴": [t: "modyfikator środkowy grawis akcent"] # 0x2f4 - - "˵": [t: "modyfikator środkowy podwójny grawis akcent"] # 0x2f5 - - "˶": [t: "modyfikator środkowy podwójny akut akcent"] # 0x2f6 - - "˷": [t: "modyfikator dolna tylda"] # 0x2f7 - - "˸": [t: "modyfikator podniesiony dwukropek"] # 0x2f8 - - "˹": [t: "modyfikator początek wysoki ton"] # 0x2f9 - - "˺": [t: "modyfikator koniec wysoki ton"] # 0x2fa - - "˻": [t: "modyfikator początek niski ton"] # 0x2fb - - "˼": [t: "modyfikator koniec niski ton"] # 0x2fc - - "˽": [t: "modyfikator półka"] # 0x2fd - - "˾": [t: "modyfikator otwórz półka"] # 0x2fe - - "˿": [t: "modyfikator dolny strzałka w lewo"] # 0x2ff - - "̀": [t: "ozdobnik akcentu grawis"] # 0x300 - - "́": [t: "ozdobnik akcentu ostrego"] # 0x301 - - "̂": [t: "ozdobnik daszka"] # 0x302 - - "̃": [t: "ozdobnik tyldy"] # 0x303 - - "̄": [t: "ozdobnik makronu"] # 0x304 - - "̅": [t: "ozdobnik kreski górnej"] # 0x305 - - "̆": [t: "ozdobnik znaku krótkości"] # 0x306 - - "̇": [t: "ozdobnik kropki nad"] # 0x307 - - "̈": [t: "ozdobnik diaerezy"] # 0x308 - - "̉": [t: "hak nad ozdobnik"] # 0x309 - - "̊": [t: "pierścień nad ozdobnik"] # 0x30a - - "̋": [t: "ozdobnik podwójnego akutu"] # 0x30b - - "̌": [t: "haczek"] # 0x30c - - "̍": [t: "ozdobnik pionowej kreski nad"] # 0x30d - - "̎": [t: "ozdobnik podwójnej pionowej kreski nad"] # 0x30e - - "̏": [t: "ozdobnik podwójnego grawisu"] # 0x30f - - "̐": [t: "ozdobnik ćandrabindu"] # 0x310 - - "̑": [t: "odwrócony ozdobnik znaku krótkości"] # 0x311 - - "̒": [t: "odwrócony przecinek nad ozdobnik"] # 0x312 - - "̓": [t: "przecinek nad ozdobnik"] # 0x313 - - "̔": [t: "odwrócony przecinek nad ozdobnik"] # 0x314 - - "̕": [t: "przecinek nad prawy ozdobnik"] # 0x315 - - "̖": [t: "ozdobnik akcentu grawis pod"] # 0x316 - - "̗": [t: "ozdobnik akcentu ostrego pod"] # 0x317 - - "̘": [t: "ozdobnik lewej pinezki pod spodem"] # 0x318 - - "̙": [t: "ozdobnik prawej pinezki pod spodem"] # 0x319 - - "̚": [t: "lewy kąt nad ozdobnik"] # 0x31a - - "̛": [t: "ozdobnik rożka"] # 0x31b - - "̜": [t: "lewa połowa pierścień pod ozdobnik"] # 0x31c - - "̝": [t: "ozdobnik górnej pinezki pod spodem"] # 0x31d - - "̞": [t: "ozdobnik dolnej pinezki pod spodem"] # 0x31e - - "̟": [t: "ozdobnik znaku plus pod spodem"] # 0x31f - - "̠": [t: "ozdobnik znaku minus pod spodem"] # 0x320 - - "̡": [t: "palatalizowany haczyk pod spodem"] # 0x321 - - "̢": [t: "retrofleksyjny haczyk pod spodem"] # 0x322 - - "̣": [t: "ozdobnik kropki pod"] # 0x323 - - "̤": [t: "ozdobnik dierezy pod spodem"] # 0x324 - - "̥": [t: "pierścień pod ozdobnik"] # 0x325 - - "̦": [t: "przecinek pod ozdobnik"] # 0x326 - - "̧": [t: "ozdobnik cedilli"] # 0x327 - - "̨": [t: "ogonek ozdobnik"] # 0x328 - - "̩": [t: "ozdobnik pionowej kreski pod spodem"] # 0x329 - - "̪": [t: "mostek pod ozdobnik"] # 0x32a - - "̫": [t: "odwrócony podwójny łuk pod spodem"] # 0x32b - - "̬": [t: "haczek pod ozdobnik"] # 0x32c - - "̭": [t: "ozdobnik daszka pod"] # 0x32d - - "̮": [t: "ozdobnik znaku krótkości pod spodem"] # 0x32e - - "̯": [t: "odwrócony ozdobnik znaku krótkości pod spodem"] # 0x32f - - "̰": [t: "tylda pod ozdobnik"] # 0x330 - - "̱": [t: "ozdobnik makronu pod"] # 0x331 - - "̲": [t: "dolny linia ozdobnik"] # 0x332 - - "̳": [t: "podwójny dolny linia ozdobnik"] # 0x333 - - "̴": [t: "tylda nakładka ozdobnik"] # 0x334 - - "̵": [t: "krótkie przekreślenie nakładane jako ozdobnik"] # 0x335 - - "̶": [t: "długie przekreślenie nakładane jako ozdobnik"] # 0x336 - - "̷": [t: "krótki ukośnik nakładka ozdobnik"] # 0x337 - - "̸": [t: "długi ukośnik nakładka ozdobnik"] # 0x338 - - "̹": [t: "prawa połowa pierścień pod ozdobnik"] # 0x339 - - "̺": [t: "odwrócony mostek pod ozdobnik"] # 0x33a - - "̻": [t: "kwadrat pod ozdobnik"] # 0x33b - - "̼": [t: "ozdobnik mewy pod spodem"] # 0x33c - - "̽": [t: "X nad ozdobnik"] # 0x33d - - "̾": [t: "pionowy ozdobnik tyldy"] # 0x33e - - "̿": [t: "ozdobnik podwójnej kreski górnej"] # 0x33f - - "̀": [t: "grawis tonowy znak ozdobnik"] # 0x340 - - "́": [t: "akut tonowy znak ozdobnik"] # 0x341 - - "͆": [t: "mostek nad"] # 0x346 + - "˜": [T: "mały tylda"] # 0x2dc + - "˝": [T: "podwójny akut akcent"] # 0x2dd + - "˞": [T: "modyfikator rotycznego haczyka"] # 0x2de + - "˟": [T: "modyfikator krzyż akcent"] # 0x2df + - "ˠ": [T: "modyfikator mały gamma"] # 0x2e0 + - "ˡ": [T: "modyfikator mały L"] # 0x2e1 + - "ˢ": [T: "modyfikator mały s"] # 0x2e2 + - "ˣ": [T: "modyfikator mały X"] # 0x2e3 + - "ˤ": [T: "modyfikator mały odwrócone zwarcie krtaniowe"] # 0x2e4 + - "˥": [T: "modyfikator kreski bardzo wysokiego tonu"] # 0x2e5 + - "˦": [T: "modyfikator kreski wysokiego tonu"] # 0x2e6 + - "˧": [T: "modyfikator środkowy tonowy kreska"] # 0x2e7 + - "˨": [T: "modyfikator kreski niskiego tonu"] # 0x2e8 + - "˩": [T: "modyfikator kreski bardzo niskiego tonu"] # 0x2e9 + - "˪": [T: "modyfikator znak tonu yin odchodzącego"] # 0x2ea + - "˫": [T: "modyfikator znak tonu yang odchodzącego"] # 0x2eb + - "ˬ": [T: "modyfikator dźwięczność"] # 0x2ec + - "˭": [T: "modyfikator nieaspirowany"] # 0x2ed + - "ˮ": [T: "modyfikator podwójny apostrof"] # 0x2ee + - "˯": [T: "modyfikator dolny grot strzałki"] # 0x2ef + - "˰": [T: "modyfikator dolny górny grot strzałki"] # 0x2f0 + - "˱": [T: "modyfikator dolny lewy grot strzałki"] # 0x2f1 + - "˲": [T: "modyfikator dolny prawy grot strzałki"] # 0x2f2 + - "˳": [T: "modyfikator dolny pierścień"] # 0x2f3 + - "˴": [T: "modyfikator środkowy grawis akcent"] # 0x2f4 + - "˵": [T: "modyfikator środkowy podwójny grawis akcent"] # 0x2f5 + - "˶": [T: "modyfikator środkowy podwójny akut akcent"] # 0x2f6 + - "˷": [T: "modyfikator dolna tylda"] # 0x2f7 + - "˸": [T: "modyfikator podniesiony dwukropek"] # 0x2f8 + - "˹": [T: "modyfikator początek wysoki ton"] # 0x2f9 + - "˺": [T: "modyfikator koniec wysoki ton"] # 0x2fa + - "˻": [T: "modyfikator początek niski ton"] # 0x2fb + - "˼": [T: "modyfikator koniec niski ton"] # 0x2fc + - "˽": [T: "modyfikator półka"] # 0x2fd + - "˾": [T: "modyfikator otwórz półka"] # 0x2fe + - "˿": [T: "modyfikator dolny strzałka w lewo"] # 0x2ff + - "̀": [T: "ozdobnik akcentu grawis"] # 0x300 + - "́": [T: "ozdobnik akcentu ostrego"] # 0x301 + - "̂": [T: "ozdobnik daszka"] # 0x302 + - "̃": [T: "ozdobnik tyldy"] # 0x303 + - "̄": [T: "ozdobnik makronu"] # 0x304 + - "̅": [T: "ozdobnik kreski górnej"] # 0x305 + - "̆": [T: "ozdobnik znaku krótkości"] # 0x306 + - "̇": [T: "ozdobnik kropki nad"] # 0x307 + - "̈": [T: "ozdobnik diaerezy"] # 0x308 + - "̉": [T: "hak nad ozdobnik"] # 0x309 + - "̊": [T: "pierścień nad ozdobnik"] # 0x30a + - "̋": [T: "ozdobnik podwójnego akutu"] # 0x30b + - "̌": [T: "haczek"] # 0x30c + - "̍": [T: "ozdobnik pionowej kreski nad"] # 0x30d + - "̎": [T: "ozdobnik podwójnej pionowej kreski nad"] # 0x30e + - "̏": [T: "ozdobnik podwójnego grawisu"] # 0x30f + - "̐": [T: "ozdobnik ćandrabindu"] # 0x310 + - "̑": [T: "odwrócony ozdobnik znaku krótkości"] # 0x311 + - "̒": [T: "odwrócony przecinek nad ozdobnik"] # 0x312 + - "̓": [T: "przecinek nad ozdobnik"] # 0x313 + - "̔": [T: "odwrócony przecinek nad ozdobnik"] # 0x314 + - "̕": [T: "przecinek nad prawy ozdobnik"] # 0x315 + - "̖": [T: "ozdobnik akcentu grawis pod"] # 0x316 + - "̗": [T: "ozdobnik akcentu ostrego pod"] # 0x317 + - "̘": [T: "ozdobnik lewej pinezki pod spodem"] # 0x318 + - "̙": [T: "ozdobnik prawej pinezki pod spodem"] # 0x319 + - "̚": [T: "lewy kąt nad ozdobnik"] # 0x31a + - "̛": [T: "ozdobnik rożka"] # 0x31b + - "̜": [T: "lewa połowa pierścień pod ozdobnik"] # 0x31c + - "̝": [T: "ozdobnik górnej pinezki pod spodem"] # 0x31d + - "̞": [T: "ozdobnik dolnej pinezki pod spodem"] # 0x31e + - "̟": [T: "ozdobnik znaku plus pod spodem"] # 0x31f + - "̠": [T: "ozdobnik znaku minus pod spodem"] # 0x320 + - "̡": [T: "palatalizowany haczyk pod spodem"] # 0x321 + - "̢": [T: "retrofleksyjny haczyk pod spodem"] # 0x322 + - "̣": [T: "ozdobnik kropki pod"] # 0x323 + - "̤": [T: "ozdobnik dierezy pod spodem"] # 0x324 + - "̥": [T: "pierścień pod ozdobnik"] # 0x325 + - "̦": [T: "przecinek pod ozdobnik"] # 0x326 + - "̧": [T: "ozdobnik cedilli"] # 0x327 + - "̨": [T: "ogonek ozdobnik"] # 0x328 + - "̩": [T: "ozdobnik pionowej kreski pod spodem"] # 0x329 + - "̪": [T: "mostek pod ozdobnik"] # 0x32a + - "̫": [T: "odwrócony podwójny łuk pod spodem"] # 0x32b + - "̬": [T: "haczek pod ozdobnik"] # 0x32c + - "̭": [T: "ozdobnik daszka pod"] # 0x32d + - "̮": [T: "ozdobnik znaku krótkości pod spodem"] # 0x32e + - "̯": [T: "odwrócony ozdobnik znaku krótkości pod spodem"] # 0x32f + - "̰": [T: "tylda pod ozdobnik"] # 0x330 + - "̱": [T: "ozdobnik makronu pod"] # 0x331 + - "̲": [T: "dolny linia ozdobnik"] # 0x332 + - "̳": [T: "podwójny dolny linia ozdobnik"] # 0x333 + - "̴": [T: "tylda nakładka ozdobnik"] # 0x334 + - "̵": [T: "krótkie przekreślenie nakładane jako ozdobnik"] # 0x335 + - "̶": [T: "długie przekreślenie nakładane jako ozdobnik"] # 0x336 + - "̷": [T: "krótki ukośnik nakładka ozdobnik"] # 0x337 + - "̸": [T: "długi ukośnik nakładka ozdobnik"] # 0x338 + - "̹": [T: "prawa połowa pierścień pod ozdobnik"] # 0x339 + - "̺": [T: "odwrócony mostek pod ozdobnik"] # 0x33a + - "̻": [T: "kwadrat pod ozdobnik"] # 0x33b + - "̼": [T: "ozdobnik mewy pod spodem"] # 0x33c + - "̽": [T: "X nad ozdobnik"] # 0x33d + - "̾": [T: "pionowy ozdobnik tyldy"] # 0x33e + - "̿": [T: "ozdobnik podwójnej kreski górnej"] # 0x33f + - "̀": [T: "grawis tonowy znak ozdobnik"] # 0x340 + - "́": [T: "akut tonowy znak ozdobnik"] # 0x341 + - "͆": [T: "mostek nad"] # 0x346 - "ΪΫϏ": # 0x3aa, 0x3ab, 0x3cf - test: @@ -205,29 +205,29 @@ if: "$SpeechOverrides_CapitalLetters = ''" then_test: if: "$Impairment = 'Blindness'" - then: [t: "wielka"] + then: [T: "wielka"] else: [x: "$SpeechOverrides_CapitalLetters"] - pitch: value: "$CapitalLetters_Pitch" replace: [spell: "translate('.', 'ΪΫϏ', 'ιυϗ')"] - - t: "z dialityką" - - "ϊ": [t: "jota z dialityką"] # 0x3ca - - "ϋ": [t: "ypsilon z dialityką"] # 0x3cb - - "ό": [t: "omikron z tonosem"] # 0x3cc - - "ύ": [t: "ypsilon z tonosem"] # 0x3cd - - "ώ": [t: "omega z tonosem"] # 0x3ce + - T: "z dialityką" + - "ϊ": [T: "jota z dialityką"] # 0x3ca + - "ϋ": [T: "ypsilon z dialityką"] # 0x3cb + - "ό": [T: "omikron z tonosem"] # 0x3cc + - "ύ": [T: "ypsilon z tonosem"] # 0x3cd + - "ώ": [T: "omega z tonosem"] # 0x3ce - "ϐ": [t: "beta"] # 0x3d0 - "ϑ": [t: "theta"] # 0x3d1 - - "ϒ": [t: "ypsilon z haczykiem"] # 0x3d2 - - "ϓ": [t: "ypsilon z akutem i haczykiem"] # 0x3d3 - - "ϔ": [t: "ypsilon z dierezą i haczykiem"] # 0x3d4 - - "ϕ": [t: "fi"] # 0x3d5 + - "ϒ": [T: "ypsilon z haczykiem"] # 0x3d2 + - "ϓ": [T: "ypsilon z akutem i haczykiem"] # 0x3d3 + - "ϔ": [T: "ypsilon z dierezą i haczykiem"] # 0x3d4 + - "ϕ": [T: "fi"] # 0x3d5 - "ϖ": [t: "pi"] # 0x3d6 - "ϗ": [t: "kai"] # 0x3d7 - - "Ϙ": [t: "wielka archaiczna koppa"] # 0x3d8 - - "ϙ": [t: "archaiczna koppa"] # 0x3d9 + - "Ϙ": [T: "wielka archaiczna koppa"] # 0x3d8 + - "ϙ": [T: "archaiczna koppa"] # 0x3d9 - "ϵ": [t: "epsilon"] # 0x3f5 - - "϶": [t: "odwrócony epsilon"] # 0x3f6 + - "϶": [T: "odwrócony epsilon"] # 0x3f6 - "А-Я": # 0x410 - 0x42f - test: if: "$CapitalLetters_Beep" @@ -241,23 +241,23 @@ if: "$SpeechOverrides_CapitalLetters = ''" then_test: if: "$Impairment = 'Blindness'" - then: [t: "wielka"] + then: [T: "wielka"] else: [x: "$SpeechOverrides_CapitalLetters"] - pitch: value: "$CapitalLetters_Pitch" replace: [spell: "translate('.', 'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ', 'абвгдежзийклмнопрстуфхцчшщъыьэюя')"] - "а": [T: "a"] # 0x430 - "б": [t: "be"] # 0x431 - - "в": [t: "we"] # 0x432 - - "г": [t: "ge"] # 0x433 + - "в": [T: "we"] # 0x432 + - "г": [T: "ge"] # 0x433 - "д": [t: "de"] # 0x434 - - "е": [t: "je"] # 0x435 - - "ж": [t: "że"] # 0x436 + - "е": [T: "je"] # 0x435 + - "ж": [T: "że"] # 0x436 - "з": [t: "ze"] # 0x437 - "и": [t: "i"] # 0x438 - - "й": [t: "krótkie i"] # 0x439 + - "й": [T: "krótkie i"] # 0x439 - "к": [t: "ka"] # 0x43a - - "л": [t: "eł"] # 0x43b + - "л": [T: "eł"] # 0x43b - "м": [t: "em"] # 0x43c - "н": [t: "en"] # 0x43d - "о": [t: "o"] # 0x43e @@ -267,29 +267,29 @@ - "т": [t: "te"] # 0x442 - "у": [t: "u"] # 0x443 - "ф": [t: "ef"] # 0x444 - - "х": [t: "cha"] # 0x445 - - "ц": [t: "ce"] # 0x446 - - "ч": [t: "cze"] # 0x447 - - "ш": [t: "sza"] # 0x448 - - "щ": [t: "szcza"] # 0x449 - - "ъ": [t: "twardy znak"] # 0x44a - - "ы": [t: "jery"] # 0x44b - - "ь": [t: "miękki znak"] # 0x44c + - "х": [T: "cha"] # 0x445 + - "ц": [T: "ce"] # 0x446 + - "ч": [T: "cze"] # 0x447 + - "ш": [T: "sza"] # 0x448 + - "щ": [T: "szcza"] # 0x449 + - "ъ": [T: "twardy znak"] # 0x44a + - "ы": [T: "jery"] # 0x44b + - "ь": [T: "miękki znak"] # 0x44c - "э": [t: "e"] # 0x44d - - "ю": [t: "ju"] # 0x44e - - "я": [t: "ja"] # 0x44f - - "؆": [t: "arabsko-indyjski pierwiastek sześcienny"] # 0x606 - - "؇": [t: "arabsko-indyjski czwarty pierwiastek"] # 0x607 - - "؈": [t: "arabski półprosta"] # 0x608 - - "‐": [t: "łącznik"] # 0x2010 - - "‑": [t: "łącznik"] # 0x2011 - - "‒": [t: "pauza cyfrowa"] # 0x2012 - - "–": [t: "półpauza"] # 0x2013 - - "—": [t: "pauza"] # 0x2014 - - "―": [t: "kreska pozioma"] # 0x2015 - - "‖": [t: "podwójna pionowa kreska"] # 0x2016 - - "†": [t: "obelisk"] # 0x2020 - - "‡": [t: "podwójny obelisk"] # 0x2021 + - "ю": [T: "ju"] # 0x44e + - "я": [T: "ja"] # 0x44f + - "؆": [T: "arabsko-indyjski pierwiastek sześcienny"] # 0x606 + - "؇": [T: "arabsko-indyjski czwarty pierwiastek"] # 0x607 + - "؈": [T: "arabski półprosta"] # 0x608 + - "‐": [T: "łącznik"] # 0x2010 + - "‑": [T: "łącznik"] # 0x2011 + - "‒": [T: "pauza cyfrowa"] # 0x2012 + - "–": [T: "półpauza"] # 0x2013 + - "—": [T: "pauza"] # 0x2014 + - "―": [T: "kreska pozioma"] # 0x2015 + - "‖": [T: "podwójna pionowa kreska"] # 0x2016 + - "†": [T: "obelisk"] # 0x2020 + - "‡": [T: "podwójny obelisk"] # 0x2021 - " - ": [T: " "] # 0x2000 - 0x2007 @@ -297,7 +297,7 @@ - test: if: "@data-chem-formula-op" then: [T: "kropka"] - else: [t: "punkt"] + else: [T: "punkt"] - "…": # 0x2026 test: @@ -306,221 +306,221 @@ # must be ClearSpeak and $ClearSpeak_Ellipses = 'AndSoOn' # speak '…' as 'and so on...' unless expr starts with '…' - "../*[1][.='…']" - then: [t: "trzy kropki"] + then: [T: "trzy kropki"] else_test: # must have $ClearSpeak_Ellipses = 'AndSoOn' if: "count(following-sibling::*) = 0" - then: [t: "i tak dalej"] - else: [t: "i tak dalej aż do"] - - - "‰": [t: "promil"] # 0x2030 - - "‱": [t: "na dziesięć tysięcy"] # 0x2031 - - "′": [t: "prim"] # 0x2032 - - "″": [t: "podwójny prim"] # 0x2033 - - "‴": [t: "potrójny prim"] # 0x2034 - - "‵": [t: "odwrócony prim"] # 0x2035 - - "‶": [t: "odwrócony podwójny prim"] # 0x2036 - - "‷": [t: "odwrócony potrójny prim"] # 0x2037 + then: [T: "i tak dalej"] + else: [T: "i tak dalej aż do"] + + - "‰": [T: "promil"] # 0x2030 + - "‱": [T: "na dziesięć tysięcy"] # 0x2031 + - "′": [T: "prim"] # 0x2032 + - "″": [T: "podwójny prim"] # 0x2033 + - "‴": [T: "potrójny prim"] # 0x2034 + - "‵": [T: "odwrócony prim"] # 0x2035 + - "‶": [T: "odwrócony podwójny prim"] # 0x2036 + - "‷": [T: "odwrócony potrójny prim"] # 0x2037 - "‸": [T: "do potęgi"] # 0x2038 - - "‹": [t: "pojedynczy lewy cudzysłów ostrokątny"] # 0x2039 - - "›": [t: "pojedynczy prawy cudzysłów ostrokątny"] # 0x203a + - "‹": [T: "pojedynczy lewy cudzysłów ostrokątny"] # 0x2039 + - "›": [T: "pojedynczy prawy cudzysłów ostrokątny"] # 0x203a - "‼": # 0x203c - test: if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" - then: [t: "podwójny wykrzyknik punkt"] # 0x203c - else: [t: "podwójny silnia"] # 0x203c + then: [T: "podwójny wykrzyknik punkt"] # 0x203c + else: [T: "podwójny silnia"] # 0x203c - "⁄": # 0x2044 - test: if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" - then: [t: "duży ukośnik"] # 0x203c + then: [T: "duży ukośnik"] # 0x203c else: [T: "podzielone przez"] # 0x203c - - "⁅": [t: "lewy kwadratowy nawias z piórem"] # 0x2045 - - "⁆": [t: "prawy kwadratowy nawias z piórem"] # 0x2046 - - "※": [t: "znak odsyłacza"] # 0x203b - - "‿": [t: "łuk dolny"] # 0x203F - - "⁀": [t: "łuk górny"] # 0x2040 - - "⁎": [t: "dolna gwiazdka"] # 0x204e - - "⁏": [t: "odwrócony średnik"] # 0x204f - - "⁐": [t: "zamknij górny"] # 0x2050 - - "⁑": [t: "dwie pionowe gwiazdki"] # 0x2051 - - "⁒": [t: "handlowy znak minus"] # 0x2052 - - "⁗": [t: "poczwórny prim"] # 0x2057 + - "⁅": [T: "lewy kwadratowy nawias z piórem"] # 0x2045 + - "⁆": [T: "prawy kwadratowy nawias z piórem"] # 0x2046 + - "※": [T: "znak odsyłacza"] # 0x203b + - "‿": [T: "łuk dolny"] # 0x203F + - "⁀": [T: "łuk górny"] # 0x2040 + - "⁎": [T: "dolna gwiazdka"] # 0x204e + - "⁏": [T: "odwrócony średnik"] # 0x204f + - "⁐": [T: "zamknij górny"] # 0x2050 + - "⁑": [T: "dwie pionowe gwiazdki"] # 0x2051 + - "⁒": [T: "handlowy znak minus"] # 0x2052 + - "⁗": [T: "poczwórny prim"] # 0x2057 - "⁠": [T: ""] # 0x2060 - - "⁰": [t: "do zerowej potęgi"] # 0x2070 - - "ⁱ": [t: "do potęgi i"] # 0x2071 - - "⁴": [t: "do czwartej potęgi"] # 0x2074 - - "⁵": [t: "do piątej potęgi"] # 0x2075 - - "⁶": [t: "do szóstej potęgi"] # 0x2076 - - "⁷": [t: "do siódmej potęgi"] # 0x2077 - - "⁸": [t: "do ósmej potęgi"] # 0x2078 - - "⁹": [t: "do dziewiątej potęgi"] # 0x2079 - - "⁺": [t: "znak plus w indeksie górnym"] # 0x207a - - "⁻": [t: "znak minus w indeksie górnym"] # 0x207b - - "⁼": [t: "znak równości w indeksie górnym"] # 0x207c - - "⁽": [t: "lewy nawias w indeksie górnym"] # 0x207d - - "⁾": [t: "prawy nawias w indeksie górnym"] # 0x207e - - "ⁿ": [t: "do potęgi n"] # 0x207f - - "₀": [t: "indeks dolny zero"] # 0x2080 - - "₁": [t: "indeks dolny jeden"] # 0x2081 - - "₂": [t: "indeks dolny dwa"] # 0x2082 - - "₃": [t: "indeks dolny trzy"] # 0x2083 - - "₄": [t: "indeks dolny cztery"] # 0x2084 - - "₅": [t: "indeks dolny pięć"] # 0x2085 - - "₆": [t: "indeks dolny sześć"] # 0x2086 - - "₇": [t: "indeks dolny siedem"] # 0x2087 - - "₈": [t: "indeks dolny osiem"] # 0x2088 - - "₉": [t: "indeks dolny dziewięć"] # 0x2089 - - "₊": [t: "znak plus w indeksie dolnym"] # 0x208a - - "₋": [t: "znak minus w indeksie dolnym"] # 0x208b - - "₌": [t: "znak równości w indeksie dolnym"] # 0x208c - - "₍": [t: "lewy nawias w indeksie dolnym"] # 0x208d - - "₎": [t: "prawy nawias w indeksie dolnym"] # 0x208e - - "ₐ": [t: "indeks dolny a"] # 0x2090 - - "ₑ": [t: "indeks dolny e"] # 0x2091 - - "ₒ": [t: "indeks dolny o"] # 0x2092 - - "ₓ": [t: "indeks dolny X"] # 0x2093 - - "ₕ": [t: "indeks dolny h"] # 0x2095 - - "ₖ": [t: "indeks dolny k"] # 0x2096 - - "ₗ": [t: "indeks dolny L"] # 0x2097 - - "ₘ": [t: "indeks dolny M"] # 0x2098 - - "ₙ": [t: "indeks dolny N"] # 0x2099 - - "ₚ": [t: "indeks dolny p"] # 0x209a - - "ₛ": [t: "indeks dolny s"] # 0x209b - - "ₜ": [t: "indeks dolny t"] # 0x209c - - "₠": [t: "europejska jednostka walutowa"] # 0x20a0 - - "₡": [t: "kolon"] # 0x20a1 - - "₢": [t: "znak cruzeiro"] # 0x20a2 - - "₣": [t: "frank"] # 0x20a3 + - "⁰": [T: "do zerowej potęgi"] # 0x2070 + - "ⁱ": [T: "do potęgi i"] # 0x2071 + - "⁴": [T: "do czwartej potęgi"] # 0x2074 + - "⁵": [T: "do piątej potęgi"] # 0x2075 + - "⁶": [T: "do szóstej potęgi"] # 0x2076 + - "⁷": [T: "do siódmej potęgi"] # 0x2077 + - "⁸": [T: "do ósmej potęgi"] # 0x2078 + - "⁹": [T: "do dziewiątej potęgi"] # 0x2079 + - "⁺": [T: "znak plus w indeksie górnym"] # 0x207a + - "⁻": [T: "znak minus w indeksie górnym"] # 0x207b + - "⁼": [T: "znak równości w indeksie górnym"] # 0x207c + - "⁽": [T: "lewy nawias w indeksie górnym"] # 0x207d + - "⁾": [T: "prawy nawias w indeksie górnym"] # 0x207e + - "ⁿ": [T: "do potęgi n"] # 0x207f + - "₀": [T: "indeks dolny zero"] # 0x2080 + - "₁": [T: "indeks dolny jeden"] # 0x2081 + - "₂": [T: "indeks dolny dwa"] # 0x2082 + - "₃": [T: "indeks dolny trzy"] # 0x2083 + - "₄": [T: "indeks dolny cztery"] # 0x2084 + - "₅": [T: "indeks dolny pięć"] # 0x2085 + - "₆": [T: "indeks dolny sześć"] # 0x2086 + - "₇": [T: "indeks dolny siedem"] # 0x2087 + - "₈": [T: "indeks dolny osiem"] # 0x2088 + - "₉": [T: "indeks dolny dziewięć"] # 0x2089 + - "₊": [T: "znak plus w indeksie dolnym"] # 0x208a + - "₋": [T: "znak minus w indeksie dolnym"] # 0x208b + - "₌": [T: "znak równości w indeksie dolnym"] # 0x208c + - "₍": [T: "lewy nawias w indeksie dolnym"] # 0x208d + - "₎": [T: "prawy nawias w indeksie dolnym"] # 0x208e + - "ₐ": [T: "indeks dolny a"] # 0x2090 + - "ₑ": [T: "indeks dolny e"] # 0x2091 + - "ₒ": [T: "indeks dolny o"] # 0x2092 + - "ₓ": [T: "indeks dolny X"] # 0x2093 + - "ₕ": [T: "indeks dolny h"] # 0x2095 + - "ₖ": [T: "indeks dolny k"] # 0x2096 + - "ₗ": [T: "indeks dolny L"] # 0x2097 + - "ₘ": [T: "indeks dolny M"] # 0x2098 + - "ₙ": [T: "indeks dolny N"] # 0x2099 + - "ₚ": [T: "indeks dolny p"] # 0x209a + - "ₛ": [T: "indeks dolny s"] # 0x209b + - "ₜ": [T: "indeks dolny t"] # 0x209c + - "₠": [T: "europejska jednostka walutowa"] # 0x20a0 + - "₡": [T: "kolon"] # 0x20a1 + - "₢": [T: "znak cruzeiro"] # 0x20a2 + - "₣": [T: "frank"] # 0x20a3 - "₤": [t: "lira"] # 0x20a4 - - "₥": [t: "mille"] # 0x20a5 + - "₥": [T: "mille"] # 0x20a5 - "₦": [t: "naira"] # 0x20a6 - "₧": [t: "peseta"] # 0x20a7 - - "₨": [t: "rupie"] # 0x20a8 + - "₨": [T: "rupie"] # 0x20a8 - "₩": [t: "won"] # 0x20a9 - - "₪": [t: "nowe szekle"] # 0x20aa + - "₪": [T: "nowe szekle"] # 0x20aa - "₫": [t: "dong"] # 0x20ab - - "€": [t: "euro"] # 0x20ac + - "€": [T: "euro"] # 0x20ac - "₭": [t: "kip"] # 0x20ad - "₮": [t: "tugrik"] # 0x20ae - "₯": [t: "drachma"] # 0x20af - - "₰": [t: "niemieckie fenigi"] # 0x20b0 - - "₱": [t: "peso"] # 0x20b1 - - "₲": [t: "guarani"] # 0x20b2 - - "₳": [t: "australe"] # 0x20b3 - - "₴": [t: "hrywny"] # 0x20b4 - - "₵": [t: "cedi"] # 0x20b5 - - "₶": [t: "liwr turoński"] # 0x20b6 + - "₰": [T: "niemieckie fenigi"] # 0x20b0 + - "₱": [T: "peso"] # 0x20b1 + - "₲": [T: "guarani"] # 0x20b2 + - "₳": [T: "australe"] # 0x20b3 + - "₴": [T: "hrywny"] # 0x20b4 + - "₵": [T: "cedi"] # 0x20b5 + - "₶": [T: "liwr turoński"] # 0x20b6 - "₷": [T: "spesmilos"] # 0x20b7 - - "₸": [t: "tenge"] # 0x20b8 - - "₹": [t: "rupie indyjskie"] # 0x20b9 - - "₺": [t: "liry tureckie"] # 0x20ba - - "⃐": [t: "lewy harpun nad ozdobnik"] # 0x20d0 - - "⃑": [t: "prawy harpun nad ozdobnik"] # 0x20d1 - - "⃒": [t: "ozdobnik długiej pionowej kreski nakładanej"] # 0x20d2 - - "⃓": [t: "ozdobnik krótkiej pionowej kreski nakładanej"] # 0x20d3 - - "⃔": [t: "strzałka nad przeciwnie do ruchu wskazówek zegara, ozdobnik"] # 0x20d4 - - "⃕": [t: "strzałka nad zgodnie z ruchem wskazówek zegara, ozdobnik"] # 0x20d5 - - "⃖": [t: "strzałka w lewo nad ozdobnik"] # 0x20d6 - - "⃗": [t: "strzałka w prawo nad ozdobnik"] # 0x20d7 - - "⃘": [t: "pierścień nakładka ozdobnik"] # 0x20d8 - - "⃙": [t: "pierścień zgodnie z ruchem wskazówek zegara, ozdobnik nakładany"] # 0x20d9 - - "⃚": [t: "pierścień przeciwnie do ruchu wskazówek zegara, ozdobnik nakładany"] # 0x20da - - "⃛": [t: "potrójny kropka"] # 0x20db - - "⃜": [t: "poczwórny kropka"] # 0x20dc - - "⃝": [t: "obejmujący okrąg ozdobnik"] # 0x20dd - - "⃞": [t: "obejmujący kwadrat ozdobnik"] # 0x20de - - "⃟": [t: "obejmujący romb ozdobnik"] # 0x20df - - "⃠": [t: "obejmujący okrąg ukośnik odwrotny ozdobnik"] # 0x20e0 - - "⃡": [t: "lewy strzałka w prawo nad ozdobnik"] # 0x20e1 - - "⃢": [t: "obejmujący ekran, ozdobnik"] # 0x20e2 - - "⃣": [t: "obejmujący nasadkę klawisza, ozdobnik"] # 0x20e3 - - "⃤": [t: "obejmujący w górę skierowany trójkąt ozdobnik"] # 0x20e4 - - "⃥": [t: "odwrotny ukośnik nakładka ozdobnik"] # 0x20e5 - - "⃦": [t: "ozdobnik podwójnej pionowej kreski"] # 0x20e6 - - "⃧": [t: "symbol renty, ozdobnik"] # 0x20e7 - - "⃨": [t: "potrójna kropka pod spodem"] # 0x20e8 - - "⃩": [t: "szeroki mostek nad, ozdobnik"] # 0x20e9 - - "⃪": [t: "strzałka w lewo nakładka ozdobnik"] # 0x20ea - - "⃫": [t: "długi podwójny ukośnik nakładka ozdobnik"] # 0x20eb - - "⃬": [t: "prawy harpun z grotem dolnym ozdobnik"] # 0x20ec - - "⃭": [t: "lewy harpun z grotem dolnym ozdobnik"] # 0x20ed - - "⃮": [t: "strzałka w lewo pod ozdobnik"] # 0x20ee - - "⃯": [t: "strzałka w prawo pod ozdobnik"] # 0x20ef - - "⃰": [t: "gwiazdka nad ozdobnik"] # 0x20f0 - - "℄": [t: "symbol linii środkowej"] # 0x2104 - - "℅": [t: "do rąk"] # 0x2105 - - "℆": [t: "każda"] # 0x2106 - - "ℇ": [t: "stała Eulera"] # 0x2107 - - "℈": [t: "skrupule"] # 0x2108 - - "℉": [t: "stopnie Fahrenheita"] # 0x2109 - - "ℊ": [t: "pisane g"] # 0x210a + - "₸": [T: "tenge"] # 0x20b8 + - "₹": [T: "rupie indyjskie"] # 0x20b9 + - "₺": [T: "liry tureckie"] # 0x20ba + - "⃐": [T: "lewy harpun nad ozdobnik"] # 0x20d0 + - "⃑": [T: "prawy harpun nad ozdobnik"] # 0x20d1 + - "⃒": [T: "ozdobnik długiej pionowej kreski nakładanej"] # 0x20d2 + - "⃓": [T: "ozdobnik krótkiej pionowej kreski nakładanej"] # 0x20d3 + - "⃔": [T: "strzałka nad przeciwnie do ruchu wskazówek zegara, ozdobnik"] # 0x20d4 + - "⃕": [T: "strzałka nad zgodnie z ruchem wskazówek zegara, ozdobnik"] # 0x20d5 + - "⃖": [T: "strzałka w lewo nad ozdobnik"] # 0x20d6 + - "⃗": [T: "strzałka w prawo nad ozdobnik"] # 0x20d7 + - "⃘": [T: "pierścień nakładka ozdobnik"] # 0x20d8 + - "⃙": [T: "pierścień zgodnie z ruchem wskazówek zegara, ozdobnik nakładany"] # 0x20d9 + - "⃚": [T: "pierścień przeciwnie do ruchu wskazówek zegara, ozdobnik nakładany"] # 0x20da + - "⃛": [T: "potrójny kropka"] # 0x20db + - "⃜": [T: "poczwórny kropka"] # 0x20dc + - "⃝": [T: "obejmujący okrąg ozdobnik"] # 0x20dd + - "⃞": [T: "obejmujący kwadrat ozdobnik"] # 0x20de + - "⃟": [T: "obejmujący romb ozdobnik"] # 0x20df + - "⃠": [T: "obejmujący okrąg ukośnik odwrotny ozdobnik"] # 0x20e0 + - "⃡": [T: "lewy strzałka w prawo nad ozdobnik"] # 0x20e1 + - "⃢": [T: "obejmujący ekran, ozdobnik"] # 0x20e2 + - "⃣": [T: "obejmujący nasadkę klawisza, ozdobnik"] # 0x20e3 + - "⃤": [T: "obejmujący w górę skierowany trójkąt ozdobnik"] # 0x20e4 + - "⃥": [T: "odwrotny ukośnik nakładka ozdobnik"] # 0x20e5 + - "⃦": [T: "ozdobnik podwójnej pionowej kreski"] # 0x20e6 + - "⃧": [T: "symbol renty, ozdobnik"] # 0x20e7 + - "⃨": [T: "potrójna kropka pod spodem"] # 0x20e8 + - "⃩": [T: "szeroki mostek nad, ozdobnik"] # 0x20e9 + - "⃪": [T: "strzałka w lewo nakładka ozdobnik"] # 0x20ea + - "⃫": [T: "długi podwójny ukośnik nakładka ozdobnik"] # 0x20eb + - "⃬": [T: "prawy harpun z grotem dolnym ozdobnik"] # 0x20ec + - "⃭": [T: "lewy harpun z grotem dolnym ozdobnik"] # 0x20ed + - "⃮": [T: "strzałka w lewo pod ozdobnik"] # 0x20ee + - "⃯": [T: "strzałka w prawo pod ozdobnik"] # 0x20ef + - "⃰": [T: "gwiazdka nad ozdobnik"] # 0x20f0 + - "℄": [T: "symbol linii środkowej"] # 0x2104 + - "℅": [T: "do rąk"] # 0x2105 + - "℆": [T: "każda"] # 0x2106 + - "ℇ": [T: "stała Eulera"] # 0x2107 + - "℈": [T: "skrupule"] # 0x2108 + - "℉": [T: "stopnie Fahrenheita"] # 0x2109 + - "ℊ": [T: "pisane g"] # 0x210a - "ℌℑℨℭ": # 0x210c, 0x2111, 0x2128, 0x212d - - t: "fraktura" + - T: "fraktura" - spell: "translate('.', 'ℌℑℨℭ', 'HIZC')" - "ℍℙℾℿ": # 0x210d, 0x2119, 0x213e, 0x213f - - t: "dwukreskowe" + - T: "dwukreskowe" - spell: "translate('.', 'ℍℙℾℿ', 'HPΓΠ')" - - "ℎ": [t: "stała Plancka"] # 0x210e - - "ℏ": [t: "h kreślone"] # 0x210f + - "ℎ": [T: "stała Plancka"] # 0x210e + - "ℏ": [T: "h kreślone"] # 0x210f - "ℐℒ℘ℬℰℱℳ": # 0x2110, 0x2112, 0x2118, 0x2130, 0x2131, 0x2133 - - t: "pisane" + - T: "pisane" - spell: "translate('.', 'ℐℒ℘ℬℰℱℳ', 'ILPBEFM')" - - "ℓ": [t: "pisane L"] # 0x2113 - - "℔": [t: "funty"] # 0x2114 - - "№": [t: "numer"] # 0x2116 - - "℥": [t: "uncje"] # 0x2125 - - "Ω": [t: "omy"] # 0x2126 + - "ℓ": [T: "pisane L"] # 0x2113 + - "℔": [T: "funty"] # 0x2114 + - "№": [T: "numer"] # 0x2116 + - "℥": [T: "uncje"] # 0x2125 + - "Ω": [T: "omy"] # 0x2126 - "℧": [T: "mho"] # 0x2127 - - "℩": [t: "odwrócony jota"] # 0x2129 - - "K": [t: "kelwin"] # 0x212a - - "Å": [t: "angstremy"] # 0x212b - - "ℯ": [t: "pisane e"] # 0x212f + - "℩": [T: "odwrócony jota"] # 0x2129 + - "K": [T: "kelwin"] # 0x212a + - "Å": [T: "angstremy"] # 0x212b + - "ℯ": [T: "pisane e"] # 0x212f # coalesced some chars that use cap letters - "Ⅎ℺⅁⅂⅃⅄": # 0x2132, 0x213a, 0x2141, 0x2142, 0x2143, 0x2144 - test: - if: "'.' = '℺'" - then: [t: "obrócone"] + then: [T: "obrócone"] - else_if: "'.' = 'Ⅎ'" - then: [t: "odwrócone"] + then: [T: "odwrócone"] - else_if: "'.' = '⅃'" - then: [t: "odwrócone bezszeryfowe"] - else: [t: "odwrócony bezszeryfowe"] + then: [T: "odwrócone bezszeryfowe"] + else: [T: "odwrócony bezszeryfowe"] - spell: "translate('.', 'Ⅎ℺⅁⅂⅃⅄', 'FQGLLY')" - - "ℴ": [t: "pisane o"] # 0x2134 - - "ℵ": [t: "pierwsza pozaskończona liczba kardynalna"] # 0x2135 - - "ℶ": [t: "druga pozaskończona liczba kardynalna"] # 0x2136 - - "ℷ": [t: "trzecia pozaskończona liczba kardynalna"] # 0x2137 - - "ℸ": [t: "czwarta pozaskończona liczba kardynalna"] # 0x2138 - - "ℼ": [t: "dwukreskowe pi"] # 0x213c - - "ℽ": [t: "dwukreskowe gamma"] # 0x213d - - "⅀": [t: "dwukreskowa suma N-arna"] # 0x2140 - - "⅋": [t: "odwrócony znak et"] # 0x214b + - "ℴ": [T: "pisane o"] # 0x2134 + - "ℵ": [T: "pierwsza pozaskończona liczba kardynalna"] # 0x2135 + - "ℶ": [T: "druga pozaskończona liczba kardynalna"] # 0x2136 + - "ℷ": [T: "trzecia pozaskończona liczba kardynalna"] # 0x2137 + - "ℸ": [T: "czwarta pozaskończona liczba kardynalna"] # 0x2138 + - "ℼ": [T: "dwukreskowe pi"] # 0x213c + - "ℽ": [T: "dwukreskowe gamma"] # 0x213d + - "⅀": [T: "dwukreskowa suma N-arna"] # 0x2140 + - "⅋": [T: "odwrócony znak et"] # 0x214b - "⅌": [T: "na"] # 0x214c - - "ⅎ": [t: "odwrócony f"] # 0x214e - - "¼": [t: "jedna czwarta"] # 0x00bc - - "½": [t: "jedna druga"] # 0x00bd - - "¾": [t: "trzy czwarte"] # 0x00be - - "⅐": [t: "jeden siódmy"] # 0x2150 - - "⅑": [t: "jeden dziewiąty"] # 0x2151 - - "⅒": [t: "jedna dziesiąta"] # 0x2152 - - "⅓": [t: "jeden trzeci"] # 0x2153 - - "⅔": [t: "dwie trzecie"] # 0x2154 - - "⅕": [t: "jeden piąty"] # 0x2155 - - "⅖": [t: "dwie piąte"] # 0x2156 - - "⅗": [t: "trzy piąte"] # 0x2157 - - "⅘": [t: "cztery piąte"] # 0x2158 - - "⅙": [t: "jeden szósty"] # 0x2159 - - "⅚": [t: "pięć szóstych"] # 0x215a - - "⅛": [t: "jeden ósmy"] # 0x215b - - "⅜": [t: "trzy ósme"] # 0x215c - - "⅝": [t: "pięć ósmych"] # 0x215d - - "⅞": [t: "siedem ósmych"] # 0x215e - - "⅟": [t: "jeden przez"] # 0x215f + - "ⅎ": [T: "odwrócony f"] # 0x214e + - "¼": [T: "jedna czwarta"] # 0x00bc + - "½": [T: "jedna druga"] # 0x00bd + - "¾": [T: "trzy czwarte"] # 0x00be + - "⅐": [T: "jeden siódmy"] # 0x2150 + - "⅑": [T: "jeden dziewiąty"] # 0x2151 + - "⅒": [T: "jedna dziesiąta"] # 0x2152 + - "⅓": [T: "jeden trzeci"] # 0x2153 + - "⅔": [T: "dwie trzecie"] # 0x2154 + - "⅕": [T: "jeden piąty"] # 0x2155 + - "⅖": [T: "dwie piąte"] # 0x2156 + - "⅗": [T: "trzy piąte"] # 0x2157 + - "⅘": [T: "cztery piąte"] # 0x2158 + - "⅙": [T: "jeden szósty"] # 0x2159 + - "⅚": [T: "pięć szóstych"] # 0x215a + - "⅛": [T: "jeden ósmy"] # 0x215b + - "⅜": [T: "trzy ósme"] # 0x215c + - "⅝": [T: "pięć ósmych"] # 0x215d + - "⅞": [T: "siedem ósmych"] # 0x215e + - "⅟": [T: "jeden przez"] # 0x215f - "Ⅰ": [t: "I"] # 0x2160 - "Ⅱ": [T: "I I"] # 0x2161 - "Ⅲ": [T: "I I I"] # 0x2162 @@ -553,170 +553,170 @@ - "ⅽ": [T: "C"] # 0x217d - "ⅾ": [T: "D"] # 0x217e - "ⅿ": [T: "M"] # 0x217f - - "↉": [t: "zero trzecich"] # 0x2189 - - "↔": [t: "lewy strzałka w prawo"] # 0x2194 - - "↕": [t: "górny strzałka w dół"] # 0x2195 - - "↖": [t: "północ zachód strzałka"] # 0x2196 + - "↉": [T: "zero trzecich"] # 0x2189 + - "↔": [T: "lewy strzałka w prawo"] # 0x2194 + - "↕": [T: "górny strzałka w dół"] # 0x2195 + - "↖": [T: "północ zachód strzałka"] # 0x2196 - "↗": # 0x2197 - test: if: "ancestor::*[2][self::m:limit]" - then: [t: "dąży od dołu"] - else: [t: "północ wschód strzałka"] + then: [T: "dąży od dołu"] + else: [T: "północ wschód strzałka"] - "↘": # 0x2198 - test: if: "ancestor::*[2][self::m:limit]" - then: [t: "dąży od góry"] - else: [t: "południe wschód strzałka"] - - - "↙": [t: "południe zachód strzałka"] # 0x2199 - - "↚": [t: "przekreślona strzałka w lewo"] # 0x219a - - "↛": [t: "przekreślona strzałka w prawo"] # 0x219b - - "↜": [t: "falista strzałka w lewo"] # 0x219c - - "↝": [t: "falista strzałka w prawo"] # 0x219d - - "↞": [t: "strzałka w lewo z dwoma grotami"] # 0x219e - - "↟": [t: "strzałka w górę z dwoma grotami"] # 0x219f - - "↠": [t: "strzałka w prawo z dwoma grotami"] # 0x21a0 - - "↡": [t: "strzałka w dół z dwoma grotami"] # 0x21a1 - - "↢": [t: "strzałka w lewo z ogon"] # 0x21a2 - - "↣": [t: "strzałka w prawo z ogon"] # 0x21a3 - - "↤": [t: "strzałka w lewo od kreski"] # 0x21a4 - - "↥": [t: "strzałka w górę od kreski"] # 0x21a5 - - "↦": [t: "strzałka w prawo od kreski"] # 0x21a6 - - "↧": [t: "strzałka w dół od kreski"] # 0x21a7 - - "↨": [t: "górny strzałka w dół z podstawa"] # 0x21a8 - - "↩": [t: "strzałka w lewo z hak"] # 0x21a9 - - "↪": [t: "strzałka w prawo z hak"] # 0x21aa - - "↫": [t: "strzałka w lewo z pętlą"] # 0x21ab - - "↬": [t: "strzałka w prawo z pętlą"] # 0x21ac - - "↭": [t: "falista strzałka w lewo i w prawo"] # 0x21ad - - "↮": [t: "przekreślona strzałka w lewo i w prawo"] # 0x21ae - - "↯": [t: "strzałka zygzakowata w dół"] # 0x21af - - "↰": [t: "strzałka w górę z tip lewy"] # 0x21b0 - - "↱": [t: "strzałka w górę z tip prawy"] # 0x21b1 - - "↲": [t: "strzałka w dół z tip lewy"] # 0x21b2 - - "↳": [t: "strzałka w dół z tip prawy"] # 0x21b3 - - "↴": [t: "strzałka w prawo z narożnikiem w dół"] # 0x21b4 - - "↵": [t: "strzałka w dół z narożnikiem w lewo"] # 0x21b5 - - "↶": [t: "strzałka po górnym półokręgu przeciwnie do ruchu wskazówek zegara"] # 0x21b6 - - "↷": [t: "strzałka po górnym półokręgu zgodnie z ruchem wskazówek zegara"] # 0x21b7 - - "↸": [t: "strzałka północno-zachodnia do długiej kreski"] # 0x21b8 - - "↹": [t: "strzałka w lewo do kreski nad strzałką w prawo do kreski"] # 0x21b9 - - "↺": [t: "strzałka po otwartym okręgu przeciwnie do ruchu wskazówek zegara"] # 0x21ba - - "↻": [t: "strzałka po otwartym okręgu zgodnie z ruchem wskazówek zegara"] # 0x21bb - - "↼": [t: "lewy harpun górny"] # 0x21bc - - "↽": [t: "lewy harpun dolny"] # 0x21bd - - "↾": [t: "górny harpun prawy"] # 0x21be - - "↿": [t: "górny harpun lewy"] # 0x21bf - - "⇀": [t: "prawy harpun górny"] # 0x21c0 - - "⇁": [t: "prawy harpun dolny"] # 0x21c1 - - "⇂": [t: "dolny harpun prawy"] # 0x21c2 - - "⇃": [t: "dolny harpun lewy"] # 0x21c3 - - "⇄": [t: "strzałka w prawo nad strzałką w lewo"] # 0x21c4 - - "⇅": [t: "strzałka w górę po lewej i strzałka w dół"] # 0x21c5 - - "⇆": [t: "strzałka w lewo nad strzałką w prawo"] # 0x21c6 - - "⇇": [t: "podwójne strzałki w lewo"] # 0x21c7 - - "⇈": [t: "podwójne strzałki w górę"] # 0x21c8 - - "⇉": [t: "podwójne strzałki w prawo"] # 0x21c9 - - "⇊": [t: "podwójne strzałki w dół"] # 0x21ca - - "⇋": [t: "lewy harpun przez prawy harpun"] # 0x21cb - - "⇌": [t: "prawy harpun przez lewy harpun"] # 0x21cc + then: [T: "dąży od góry"] + else: [T: "południe wschód strzałka"] + + - "↙": [T: "południe zachód strzałka"] # 0x2199 + - "↚": [T: "przekreślona strzałka w lewo"] # 0x219a + - "↛": [T: "przekreślona strzałka w prawo"] # 0x219b + - "↜": [T: "falista strzałka w lewo"] # 0x219c + - "↝": [T: "falista strzałka w prawo"] # 0x219d + - "↞": [T: "strzałka w lewo z dwoma grotami"] # 0x219e + - "↟": [T: "strzałka w górę z dwoma grotami"] # 0x219f + - "↠": [T: "strzałka w prawo z dwoma grotami"] # 0x21a0 + - "↡": [T: "strzałka w dół z dwoma grotami"] # 0x21a1 + - "↢": [T: "strzałka w lewo z ogon"] # 0x21a2 + - "↣": [T: "strzałka w prawo z ogon"] # 0x21a3 + - "↤": [T: "strzałka w lewo od kreski"] # 0x21a4 + - "↥": [T: "strzałka w górę od kreski"] # 0x21a5 + - "↦": [T: "strzałka w prawo od kreski"] # 0x21a6 + - "↧": [T: "strzałka w dół od kreski"] # 0x21a7 + - "↨": [T: "górny strzałka w dół z podstawa"] # 0x21a8 + - "↩": [T: "strzałka w lewo z hak"] # 0x21a9 + - "↪": [T: "strzałka w prawo z hak"] # 0x21aa + - "↫": [T: "strzałka w lewo z pętlą"] # 0x21ab + - "↬": [T: "strzałka w prawo z pętlą"] # 0x21ac + - "↭": [T: "falista strzałka w lewo i w prawo"] # 0x21ad + - "↮": [T: "przekreślona strzałka w lewo i w prawo"] # 0x21ae + - "↯": [T: "strzałka zygzakowata w dół"] # 0x21af + - "↰": [T: "strzałka w górę z tip lewy"] # 0x21b0 + - "↱": [T: "strzałka w górę z tip prawy"] # 0x21b1 + - "↲": [T: "strzałka w dół z tip lewy"] # 0x21b2 + - "↳": [T: "strzałka w dół z tip prawy"] # 0x21b3 + - "↴": [T: "strzałka w prawo z narożnikiem w dół"] # 0x21b4 + - "↵": [T: "strzałka w dół z narożnikiem w lewo"] # 0x21b5 + - "↶": [T: "strzałka po górnym półokręgu przeciwnie do ruchu wskazówek zegara"] # 0x21b6 + - "↷": [T: "strzałka po górnym półokręgu zgodnie z ruchem wskazówek zegara"] # 0x21b7 + - "↸": [T: "strzałka północno-zachodnia do długiej kreski"] # 0x21b8 + - "↹": [T: "strzałka w lewo do kreski nad strzałką w prawo do kreski"] # 0x21b9 + - "↺": [T: "strzałka po otwartym okręgu przeciwnie do ruchu wskazówek zegara"] # 0x21ba + - "↻": [T: "strzałka po otwartym okręgu zgodnie z ruchem wskazówek zegara"] # 0x21bb + - "↼": [T: "lewy harpun górny"] # 0x21bc + - "↽": [T: "lewy harpun dolny"] # 0x21bd + - "↾": [T: "górny harpun prawy"] # 0x21be + - "↿": [T: "górny harpun lewy"] # 0x21bf + - "⇀": [T: "prawy harpun górny"] # 0x21c0 + - "⇁": [T: "prawy harpun dolny"] # 0x21c1 + - "⇂": [T: "dolny harpun prawy"] # 0x21c2 + - "⇃": [T: "dolny harpun lewy"] # 0x21c3 + - "⇄": [T: "strzałka w prawo nad strzałką w lewo"] # 0x21c4 + - "⇅": [T: "strzałka w górę po lewej i strzałka w dół"] # 0x21c5 + - "⇆": [T: "strzałka w lewo nad strzałką w prawo"] # 0x21c6 + - "⇇": [T: "podwójne strzałki w lewo"] # 0x21c7 + - "⇈": [T: "podwójne strzałki w górę"] # 0x21c8 + - "⇉": [T: "podwójne strzałki w prawo"] # 0x21c9 + - "⇊": [T: "podwójne strzałki w dół"] # 0x21ca + - "⇋": [T: "lewy harpun przez prawy harpun"] # 0x21cb + - "⇌": [T: "prawy harpun przez lewy harpun"] # 0x21cc - "⇍": [T: "nie wynika z"] # 0x21cd - "⇎": [T: "nie jest równoważne"] # 0x21ce - "⇏": [T: "nie implikuje"] # 0x21cf - "⇐": [T: "wynika z"] # 0x21d0 - - "⇑": [t: "podwójna strzałka w górę"] # 0x21d1 - - "⇒": [t: "implikuje"] # 0x21d2 - - "⇓": [t: "podwójna strzałka w dół"] # 0x21d3 - - "⇔": [t: "wtedy i tylko wtedy"] # 0x21d4 - - "⇕": [t: "podwójna strzałka w górę i w dół"] # 0x21d5 - - "⇖": [t: "podwójna strzałka na północny zachód"] # 0x21d6 - - "⇗": [t: "podwójna strzałka na północny wschód"] # 0x21d7 - - "⇘": [t: "podwójna strzałka na południowy wschód"] # 0x21d8 - - "⇙": [t: "podwójna strzałka na południowy zachód"] # 0x21d9 - - "⇚": [t: "lewy potrójny strzałka"] # 0x21da - - "⇛": [t: "prawy potrójny strzałka"] # 0x21db - - "⇜": [t: "falista strzałka w lewo"] # 0x21dc - - "⇝": [t: "falista strzałka w prawo"] # 0x21dd - - "⇞": [t: "strzałka w górę z podwójną kreską"] # 0x21de - - "⇟": [t: "strzałka w dół z podwójną kreską"] # 0x21df - - "⇠": [t: "przerywana strzałka w lewo"] # 0x21e0 - - "⇡": [t: "przerywana strzałka w górę"] # 0x21e1 - - "⇢": [t: "przerywana strzałka w prawo"] # 0x21e2 - - "⇣": [t: "przerywana strzałka w dół"] # 0x21e3 - - "⇤": [t: "strzałka w lewo do kreski"] # 0x21e4 - - "⇥": [t: "strzałka w prawo do kreski"] # 0x21e5 - - "⇦": [t: "biała strzałka w lewo"] # 0x21e6 - - "⇧": [t: "biała strzałka w górę"] # 0x21e7 - - "⇨": [t: "biała strzałka w prawo"] # 0x21e8 - - "⇩": [t: "biała strzałka w dół"] # 0x21e9 - - "⇪": [t: "biała strzałka w górę od kreski"] # 0x21ea - - "⇫": [t: "górna biała strzałka na podstawie"] # 0x21eb - - "⇬": [t: "górna biała strzałka na podstawie z kreską poziomą"] # 0x21ec - - "⇭": [t: "górna biała strzałka na podstawie z kreską pionową"] # 0x21ed - - "⇮": [t: "górny biały podwójna strzałka"] # 0x21ee - - "⇯": [t: "górna biała podwójna strzałka na podstawie"] # 0x21ef - - "⇰": [t: "biała strzałka w prawo od ściany"] # 0x21f0 - - "⇱": [t: "strzałka północno-zachodnia do narożnika"] # 0x21f1 - - "⇲": [t: "strzałka południowo-wschodnia do narożnika"] # 0x21f2 - - "⇳": [t: "biała strzałka w górę i w dół"] # 0x21f3 - - "⇴": [t: "strzałka w prawo z małym okręgiem"] # 0x21f4 - - "⇵": [t: "strzałka w dół po lewej od strzałki w górę"] # 0x21f5 - - "⇶": [t: "trzy strzałki w prawo"] # 0x21f6 - - "⇷": [t: "strzałka w lewo z pionową kreską"] # 0x21f7 - - "⇸": [t: "strzałka w prawo z pionową kreską"] # 0x21f8 - - "⇹": [t: "strzałki w lewo i w prawo z pionową kreską"] # 0x21f9 - - "⇺": [t: "strzałka w lewo z podwójną pionową kreską"] # 0x21fa - - "⇻": [t: "strzałka w prawo z podwójną pionową kreską"] # 0x21fb - - "⇼": [t: "strzałki w lewo i w prawo z podwójną pionową kreską"] # 0x21fc - - "⇽": [t: "strzałka w lewo z otwartym grotem"] # 0x21fd - - "⇾": [t: "strzałka w prawo z otwartym grotem"] # 0x21fe - - "⇿": [t: "strzałka w lewo i w prawo z otwartymi grotami"] # 0x21ff - - "∀": [t: "dla każdego"] # 0x2200 + - "⇑": [T: "podwójna strzałka w górę"] # 0x21d1 + - "⇒": [T: "implikuje"] # 0x21d2 + - "⇓": [T: "podwójna strzałka w dół"] # 0x21d3 + - "⇔": [T: "wtedy i tylko wtedy"] # 0x21d4 + - "⇕": [T: "podwójna strzałka w górę i w dół"] # 0x21d5 + - "⇖": [T: "podwójna strzałka na północny zachód"] # 0x21d6 + - "⇗": [T: "podwójna strzałka na północny wschód"] # 0x21d7 + - "⇘": [T: "podwójna strzałka na południowy wschód"] # 0x21d8 + - "⇙": [T: "podwójna strzałka na południowy zachód"] # 0x21d9 + - "⇚": [T: "lewy potrójny strzałka"] # 0x21da + - "⇛": [T: "prawy potrójny strzałka"] # 0x21db + - "⇜": [T: "falista strzałka w lewo"] # 0x21dc + - "⇝": [T: "falista strzałka w prawo"] # 0x21dd + - "⇞": [T: "strzałka w górę z podwójną kreską"] # 0x21de + - "⇟": [T: "strzałka w dół z podwójną kreską"] # 0x21df + - "⇠": [T: "przerywana strzałka w lewo"] # 0x21e0 + - "⇡": [T: "przerywana strzałka w górę"] # 0x21e1 + - "⇢": [T: "przerywana strzałka w prawo"] # 0x21e2 + - "⇣": [T: "przerywana strzałka w dół"] # 0x21e3 + - "⇤": [T: "strzałka w lewo do kreski"] # 0x21e4 + - "⇥": [T: "strzałka w prawo do kreski"] # 0x21e5 + - "⇦": [T: "biała strzałka w lewo"] # 0x21e6 + - "⇧": [T: "biała strzałka w górę"] # 0x21e7 + - "⇨": [T: "biała strzałka w prawo"] # 0x21e8 + - "⇩": [T: "biała strzałka w dół"] # 0x21e9 + - "⇪": [T: "biała strzałka w górę od kreski"] # 0x21ea + - "⇫": [T: "górna biała strzałka na podstawie"] # 0x21eb + - "⇬": [T: "górna biała strzałka na podstawie z kreską poziomą"] # 0x21ec + - "⇭": [T: "górna biała strzałka na podstawie z kreską pionową"] # 0x21ed + - "⇮": [T: "górny biały podwójna strzałka"] # 0x21ee + - "⇯": [T: "górna biała podwójna strzałka na podstawie"] # 0x21ef + - "⇰": [T: "biała strzałka w prawo od ściany"] # 0x21f0 + - "⇱": [T: "strzałka północno-zachodnia do narożnika"] # 0x21f1 + - "⇲": [T: "strzałka południowo-wschodnia do narożnika"] # 0x21f2 + - "⇳": [T: "biała strzałka w górę i w dół"] # 0x21f3 + - "⇴": [T: "strzałka w prawo z małym okręgiem"] # 0x21f4 + - "⇵": [T: "strzałka w dół po lewej od strzałki w górę"] # 0x21f5 + - "⇶": [T: "trzy strzałki w prawo"] # 0x21f6 + - "⇷": [T: "strzałka w lewo z pionową kreską"] # 0x21f7 + - "⇸": [T: "strzałka w prawo z pionową kreską"] # 0x21f8 + - "⇹": [T: "strzałki w lewo i w prawo z pionową kreską"] # 0x21f9 + - "⇺": [T: "strzałka w lewo z podwójną pionową kreską"] # 0x21fa + - "⇻": [T: "strzałka w prawo z podwójną pionową kreską"] # 0x21fb + - "⇼": [T: "strzałki w lewo i w prawo z podwójną pionową kreską"] # 0x21fc + - "⇽": [T: "strzałka w lewo z otwartym grotem"] # 0x21fd + - "⇾": [T: "strzałka w prawo z otwartym grotem"] # 0x21fe + - "⇿": [T: "strzałka w lewo i w prawo z otwartymi grotami"] # 0x21ff + - "∀": [T: "dla każdego"] # 0x2200 - "∁": # 0x2201 - - t: "dopełnienie" + - T: "dopełnienie" - "∂": # 0x2202 - test: if: "$Verbosity='Terse'" - then: [t: "cząstkowe"] - else: [t: "pochodna cząstkowa"] - - "∃": [t: "istnieje"] # 0x2203 - - "∄": [t: "nie istnieje"] # 0x2204 + then: [T: "cząstkowe"] + else: [T: "pochodna cząstkowa"] + - "∃": [T: "istnieje"] # 0x2203 + - "∄": [T: "nie istnieje"] # 0x2204 - "∅": [T: "zbiór pusty"] # 0x2205 - "∆": # 0x2206 - test: if: "following-sibling::*" then_test: if: "$Verbosity!='Terse'" - then: [t: "laplasjan"] # "LahPlahsian" sounds better than "laplacian" in speech engines tested - else: [t: "laplasjan"] - else: [t: "laplasjan"] + then: [T: "laplasjan"] # "LahPlahsian" sounds better than "laplacian" in speech engines tested + else: [T: "laplasjan"] + else: [T: "laplasjan"] - - "∇": [t: "nabla"] # 0x2207 + - "∇": [T: "nabla"] # 0x2207 - "∉": # 0x2209 # rule is identical to 0x2208 - test: if: "$SpeechStyle != 'ClearSpeak'" - then: [t: "nie jest elementem zbioru"] + then: [T: "nie jest elementem zbioru"] # 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: "nie należy do"] + then: [T: "nie należy do"] - else_if: $ClearSpeak_SetMemberSymbol = 'Member' - then: [t: "nie jest elementem zbioru"] + then: [T: "nie jest elementem zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'Element' - then: [t: "nie jest elementem zbioru"] - - else: [t: "nienależące do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + then: [T: "nie jest elementem zbioru"] + - else: [T: "nienależące do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' else_test: - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' - then: [t: "nie jest elementem zbioru"] + then: [T: "nie jest elementem zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'Element' - then: [t: "nie jest elementem zbioru"] + then: [T: "nie jest elementem zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'In' - then: [t: "nie należy do"] - - else: [t: "nie należy do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + then: [T: "nie należy do"] + - else: [T: "nie należy do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' - "∊": # 0x220a - test: if: "$SpeechStyle != 'ClearSpeak'" @@ -724,7 +724,7 @@ - test: if: "$Verbosity!='Terse' and not(parent::m:set)" # "the set x is an element of ..." sounds bad then: [T: "jest"] - - t: "element zbioru" + - T: "element zbioru" # 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 @@ -732,32 +732,32 @@ - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'In' then: [T: "do"] - else_if: $ClearSpeak_SetMemberSymbol = 'Member' - then: [t: "element zbioru"] + then: [T: "element zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'Element' - then: [t: "należy do"] - - else: [t: "należące do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + then: [T: "należy do"] + - else: [T: "należące do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' else_test: - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' - then: [t: "jest elementem zbioru"] + then: [T: "jest elementem zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'Element' - then: [t: "jest elementem zbioru"] + then: [T: "jest elementem zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'In' - then: [t: "należy do"] - - else: [t: "należy do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' - - "∋": [t: "zawiera element"] # 0x220b - - "∌": [t: "nie zawiera elementu"] # 0x220c - - "∍": [t: "zawiera element"] # 0x220d - - "∎": [t: "koniec dowodu"] # 0x220e + then: [T: "należy do"] + - else: [T: "należy do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + - "∋": [T: "zawiera element"] # 0x220b + - "∌": [T: "nie zawiera elementu"] # 0x220c + - "∍": [T: "zawiera element"] # 0x220d + - "∎": [T: "koniec dowodu"] # 0x220e - "∏": [T: "iloczyn"] # 0x220f - - "∐": [t: "koprodukt"] # 0x2210 + - "∐": [T: "koprodukt"] # 0x2210 - "∑": [T: "suma"] # 0x2211 - "−": [T: "minus"] # 0x2212 - "∓": [T: "minus plus"] # 0x2213 - - "∔": [t: "kropka plus"] # 0x2214 + - "∔": [T: "kropka plus"] # 0x2214 - "∕": [T: "podzielone przez"] # 0x2215 - - "∖": [t: "zbiór minus"] # 0x2216 + - "∖": [T: "zbiór minus"] # 0x2216 - "∗": [T: "razy"] # 0x2217 - - "∘": [t: "złożone z"] # 0x2218 + - "∘": [T: "złożone z"] # 0x2218 - "∙": # 0x2219 - test: if: "@data-chem-formula-op" @@ -765,71 +765,71 @@ else: [T: "razy"] - "√": # 0x221a - - t: "pierwiastek kwadratowy z" + - T: "pierwiastek kwadratowy z" - "∛": # 0x221b - - t: "pierwiastek sześcienny z" + - T: "pierwiastek sześcienny z" - "∜": # 0x221c - - t: "czwarty pierwiastek z" + - T: "czwarty pierwiastek z" - "∝": # 0x221d - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "proporcjonalne do" + - T: "proporcjonalne do" - "∞": - test: if: "ancestor::*[self::m:limit or self::m:limit-sup or self::m:limit-inf or self::m:large-op]" then: [T: "nieskończoności"] else: [T: "nieskończoność"] # 0x221e - - "∟": [t: "kąt prosty"] # 0x221f - - "∠": [t: "kąt"] # 0x2220 - - "∡": [t: "kąt mierzony"] # 0x2221 - - "∢": [t: "kąt sferyczny"] # 0x2222 + - "∟": [T: "kąt prosty"] # 0x221f + - "∠": [T: "kąt"] # 0x2220 + - "∡": [T: "kąt mierzony"] # 0x2221 + - "∢": [T: "kąt sferyczny"] # 0x2222 - "∣": [T: "dzieli"] # 0x2223 - - "∤": [t: "nie dzieli"] # 0x2224 + - "∤": [T: "nie dzieli"] # 0x2224 - "∧": [T: "i"] # 0x2227 - "∨": [T: "lub"] # 0x2228 - "∩": [T: "przecięcie"] # 0x2229 - "∪": [T: "suma zbiorów"] # 0x222a - "∫": [T: "całka"] # 0x222b - - "∬": [t: "całka podwójna"] # 0x222c - - "∭": [t: "całka potrójna"] # 0x222d - - "∮": [t: "całka krzywoliniowa"] # 0x222e - - "∯": [t: "całka powierzchniowa"] # 0x222f - - "∰": [t: "całka objętościowa"] # 0x2230 - - "∱": [t: "całka zgodnie z ruchem wskazówek zegara"] # 0x2231 - - "∲": [t: "całka krzywoliniowa zgodnie z ruchem wskazówek zegara"] # 0x2232 - - "∳": [t: "całka krzywoliniowa przeciwnie do ruchu wskazówek zegara"] # 0x2233 - - "∴": [t: "zatem"] # 0x2234 - - "∵": [t: "ponieważ"] # 0x2235 + - "∬": [T: "całka podwójna"] # 0x222c + - "∭": [T: "całka potrójna"] # 0x222d + - "∮": [T: "całka krzywoliniowa"] # 0x222e + - "∯": [T: "całka powierzchniowa"] # 0x222f + - "∰": [T: "całka objętościowa"] # 0x2230 + - "∱": [T: "całka zgodnie z ruchem wskazówek zegara"] # 0x2231 + - "∲": [T: "całka krzywoliniowa zgodnie z ruchem wskazówek zegara"] # 0x2232 + - "∳": [T: "całka krzywoliniowa przeciwnie do ruchu wskazówek zegara"] # 0x2233 + - "∴": [T: "zatem"] # 0x2234 + - "∵": [T: "ponieważ"] # 0x2235 - "∶": # 0x2236 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - T: "do" - - "∷": [t: "jako"] # 0x2237 - - "∸": [t: "kropka minus"] # 0x2238 - - "∹": [t: "ma nadmiar względem"] # 0x2239 + - "∷": [T: "jako"] # 0x2237 + - "∸": [T: "kropka minus"] # 0x2238 + - "∹": [T: "ma nadmiar względem"] # 0x2239 - "∺": # 0x223a - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "geometrycznie proporcjonalne do" + - T: "geometrycznie proporcjonalne do" - "∻": # 0x223b - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "homotetyczne do" - - "∼": [t: "tylda"] # 0x223c - - "∽": [t: "odwrócona tylda"] # 0x223d + - T: "homotetyczne do" + - "∼": [T: "tylda"] # 0x223c + - "∽": [T: "odwrócona tylda"] # 0x223d - "∾": # 0x223e - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "most dodatnie" - - "∿": [t: "fala sinusoidalna"] # 0x223f - - "≀": [t: "iloczyn wieńcowy"] # 0x2240 - - "≁": [t: "nie jest podobne"] # 0x2241 - - "≂": [t: "minus tylda"] # 0x2242 + - T: "most dodatnie" + - "∿": [T: "fala sinusoidalna"] # 0x223f + - "≀": [T: "iloczyn wieńcowy"] # 0x2240 + - "≁": [T: "nie jest podobne"] # 0x2241 + - "≂": [T: "minus tylda"] # 0x2242 - "≃": # 0x2243 - test: if: "$Verbosity!='Terse'" @@ -844,7 +844,7 @@ - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "przystające do" + - T: "przystające do" - "≆": # 0x2246 - test: if: "$Verbosity!='Terse'" @@ -854,7 +854,7 @@ - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "nie przystające do" + - T: "nie przystające do" - "≈": # 0x2248 - test: if: "$Verbosity!='Terse'" @@ -870,21 +870,21 @@ if: "$Verbosity!='Terse'" then: [T: "jest"] - T: "w przybliżeniu równe lub równe" - - "≋": [t: "potrójny tylda"] # 0x224b + - "≋": [T: "potrójny tylda"] # 0x224b - "≌": [T: "wszystkie równe"] # 0x224c - "≍": # 0x224d - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "Równoważne" + - T: "Równoważne" - "≎": # 0x224e - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - T: "geometrycznie równoważne" - "≏": # 0x224f - - t: "różnica między" - - "≐": [t: "dąży do granicy"] # 0x2250 + - T: "różnica między" + - "≐": [T: "dąży do granicy"] # 0x2250 - "≑": # 0x2251 - test: if: "$Verbosity!='Terse'" @@ -898,32 +898,32 @@ - "≓": # 0x2253 - test: if: "$Verbosity!='Terse'" - then: [t: "jest"] + then: [T: "jest"] - T: "obrazem lub w przybliżeniu równe" - - "≔": [t: "dwukropek równa się"] # 0x2254 - - "≕": [t: "równa się dwukropek"] # 0x2255 - - "≖": [t: "pierścień w znaku równości"] # 0x2256 + - "≔": [T: "dwukropek równa się"] # 0x2254 + - "≕": [T: "równa się dwukropek"] # 0x2255 + - "≖": [T: "pierścień w znaku równości"] # 0x2256 - "≗": # 0x2257 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - T: "w przybliżeniu równe" - - "≘": [t: "odpowiada"] # 0x2258 - - "≙": [t: "estymuje"] # 0x2259 + - "≘": [T: "odpowiada"] # 0x2258 + - "≙": [T: "estymuje"] # 0x2259 - "≚": # 0x225a - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "równokątne do" - - "≛": [t: "gwiazda równa się"] # 0x225b - - "≜": [t: "delta równa się"] # 0x225c - - "≝": [t: "jest z definicji równe"] # 0x225d + - T: "równokątne do" + - "≛": [T: "gwiazda równa się"] # 0x225b + - "≜": [T: "delta równa się"] # 0x225c + - "≝": [T: "jest z definicji równe"] # 0x225d - "≞": # 0x225e - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "mierzone na" - - "≟": [t: "ma nieznaną relację z"] # 0x225f + - T: "mierzone na" + - "≟": [T: "ma nieznaną relację z"] # 0x225f - "≠": # 0x2260 - test: if: "$Verbosity!='Terse'" @@ -933,209 +933,209 @@ - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "identyczne z" + - T: "identyczne z" - "≢": # 0x2262 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "nie identyczne z" + - T: "nie identyczne z" - "≣": # 0x2263 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "ściśle równoważne" - - "≦": [t: "mniejsze nad równe"] # 0x2266 - - "≧": [t: "większe nad równe"] # 0x2267 + - T: "ściśle równoważne" + - "≦": [T: "mniejsze nad równe"] # 0x2266 + - "≧": [T: "większe nad równe"] # 0x2267 - "≨": # 0x2268 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "mniejsze niż ale nie równa się" + - T: "mniejsze niż ale nie równa się" - "≩": # 0x2269 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "większe niż ale nie równa się" + - T: "większe niż ale nie równa się" - "≪": # 0x226a - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "znacznie mniejsze niż" + - T: "znacznie mniejsze niż" - "≫": # 0x226b - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "znacznie większe niż" + - T: "znacznie większe niż" - "≬": # 0x226c - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "między" + - T: "między" - "≭": # 0x226d - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "nie równoważne" + - T: "nie równoważne" - "≮": # 0x226e - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "nie mniejsze niż" + - T: "nie mniejsze niż" - "≯": # 0x226f - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "nie większe niż" + - T: "nie większe niż" - "≰": # 0x2270 - - t: "nie jest mniejsze ani równe" + - T: "nie jest mniejsze ani równe" - "≱": # 0x2271 - - t: "nie jest większe ani równe" + - T: "nie jest większe ani równe" - "≲": # 0x2272 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "mniejsze niż lub równoważne" + - T: "mniejsze niż lub równoważne" - "≳": # 0x2273 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "większe niż lub równoważne" + - T: "większe niż lub równoważne" - "≴": # 0x2274 - - t: "nie jest mniejsze ani równoważne" + - T: "nie jest mniejsze ani równoważne" - "≵": # 0x2275 - - t: "nie jest większe ani równoważne" + - T: "nie jest większe ani równoważne" - "≶": # 0x2276 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "mniejsze lub większe niż" + - T: "mniejsze lub większe niż" - "≷": # 0x2277 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "większe lub mniejsze niż" + - T: "większe lub mniejsze niż" - "≸": # 0x2278 - - t: "nie jest mniejsze ani większe niż" + - T: "nie jest mniejsze ani większe niż" - "≹": # 0x2279 - - t: "nie jest większe ani mniejsze niż" - - "≺": [t: "poprzedza"] # 0x227a - - "≻": [t: "następuje po"] # 0x227b + - T: "nie jest większe ani mniejsze niż" + - "≺": [T: "poprzedza"] # 0x227a + - "≻": [T: "następuje po"] # 0x227b - "≼": [T: "poprzedza lub jest równe"] # 0x227c - "≽": [T: "następuje po lub jest równe"] # 0x227d - - "≾": [t: "poprzedza lub jest równoważne"] # 0x227e - - "≿": [t: "następuje po lub jest równoważne"] # 0x227f + - "≾": [T: "poprzedza lub jest równoważne"] # 0x227e + - "≿": [T: "następuje po lub jest równoważne"] # 0x227f - "⊀": [T: "nie poprzedza"] # 0x2280 - "⊁": [T: "nie następuje po"] # 0x2281 - "⊂": # 0x2282 - test: if: "$Verbosity!='Terse'" - then: [t: "jest"] - - t: "podzbiór" + then: [T: "jest"] + - T: "podzbiór" - "⊃": # 0x2283 - test: if: "$Verbosity!='Terse'" - then: [t: "jest"] - - t: "nadzbiór" + then: [T: "jest"] + - T: "nadzbiór" - "⊄": # 0x2284 - - t: "nie jest podzbiorem" + - T: "nie jest podzbiorem" - "⊅": # 0x2285 - - t: "nie jest nadzbiorem" + - T: "nie jest nadzbiorem" - "⊆": # 0x2286 - test: if: "$Verbosity!='Terse'" - then: [t: "jest"] - - t: "podzbiór lub równy" + then: [T: "jest"] + - T: "podzbiór lub równy" - "⊇": # 0x2287 - test: if: "$Verbosity!='Terse'" - then: [t: "jest"] - - t: "nadzbiór lub równy" + then: [T: "jest"] + - T: "nadzbiór lub równy" - "⊈": # 0x2288 - - t: "nie jest podzbiorem ani nie jest równe" + - T: "nie jest podzbiorem ani nie jest równe" - "⊉": # 0x2289 - - t: "nie jest nadzbiorem ani nie jest równe" - - "⊊": [t: "podzbiór z nie równa się"] # 0x228a - - "⊋": [t: "nadzbiór z nie równa się"] # 0x228b - - "⊌": [t: "wielozbiór"] # 0x228c - - "⊍": [t: "mnożenie wielozbiorów"] # 0x228d - - "⊎": [t: "suma wielozbiorów"] # 0x228e + - T: "nie jest nadzbiorem ani nie jest równe" + - "⊊": [T: "podzbiór z nie równa się"] # 0x228a + - "⊋": [T: "nadzbiór z nie równa się"] # 0x228b + - "⊌": [T: "wielozbiór"] # 0x228c + - "⊍": [T: "mnożenie wielozbiorów"] # 0x228d + - "⊎": [T: "suma wielozbiorów"] # 0x228e - "⊏": [T: "kwadratowo poprzedza"] # 0x228f - "⊐": [T: "kwadratowo następuje po"] # 0x2290 - "⊑": [T: "kwadratowo poprzedza lub jest równe"] # 0x2291 - "⊒": [T: "kwadratowo następuje po lub jest równe"] # 0x2292 - - "⊓": [t: "kwadratowa część wspólna"] # 0x2293 - - "⊔": [t: "kwadratowa suma zbiorów"] # 0x2294 - - "⊕": [t: "w kółku plus"] # 0x2295 - - "⊖": [t: "w kółku minus"] # 0x2296 - - "⊗": [t: "w kółku razy"] # 0x2297 - - "⊘": [t: "w kółku ukośnik"] # 0x2298 - - "⊙": [t: "kropka w kółku"] # 0x2299 - - "⊚": [t: "pierścień w kółku"] # 0x229a - - "⊛": [t: "gwiazdka w kółku"] # 0x229b - - "⊜": [t: "równa się w kółku"] # 0x229c - - "⊝": [t: "kreska w kółku"] # 0x229d - - "⊞": [t: "plus w kwadracie"] # 0x229e - - "⊟": [t: "minus w kwadracie"] # 0x229f - - "⊠": [t: "razy w kwadracie"] # 0x22a0 - - "⊡": [t: "kropka w kwadracie"] # 0x22a1 - - "⊢": [t: "dowodzi"] # 0x22a2 - - "⊣": [t: "jest dowodzone przez"] # 0x22a3 - - "⊤": [t: "góra"] # 0x22a4 - - "⊦": [t: "redukuje się do"] # 0x22a6 - - "⊧": [t: "modeluje"] # 0x22a7 + - "⊓": [T: "kwadratowa część wspólna"] # 0x2293 + - "⊔": [T: "kwadratowa suma zbiorów"] # 0x2294 + - "⊕": [T: "w kółku plus"] # 0x2295 + - "⊖": [T: "w kółku minus"] # 0x2296 + - "⊗": [T: "w kółku razy"] # 0x2297 + - "⊘": [T: "w kółku ukośnik"] # 0x2298 + - "⊙": [T: "kropka w kółku"] # 0x2299 + - "⊚": [T: "pierścień w kółku"] # 0x229a + - "⊛": [T: "gwiazdka w kółku"] # 0x229b + - "⊜": [T: "równa się w kółku"] # 0x229c + - "⊝": [T: "kreska w kółku"] # 0x229d + - "⊞": [T: "plus w kwadracie"] # 0x229e + - "⊟": [T: "minus w kwadracie"] # 0x229f + - "⊠": [T: "razy w kwadracie"] # 0x22a0 + - "⊡": [T: "kropka w kwadracie"] # 0x22a1 + - "⊢": [T: "dowodzi"] # 0x22a2 + - "⊣": [T: "jest dowodzone przez"] # 0x22a3 + - "⊤": [T: "góra"] # 0x22a4 + - "⊦": [T: "redukuje się do"] # 0x22a6 + - "⊧": [T: "modeluje"] # 0x22a7 - "⊨": # 0x22a8 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "prawdziwe" - - "⊩": [t: "wymusza"] # 0x22a9 - - "⊪": [t: "potrójna pionowa kreska z prawym znacznikiem dedukcji"] # 0x22aa - - "⊫": [t: "podwójna pionowa kreska z podwójnym prawym znacznikiem dedukcji"] # 0x22ab - - "⊬": [t: "nie dowodzi"] # 0x22ac + - T: "prawdziwe" + - "⊩": [T: "wymusza"] # 0x22a9 + - "⊪": [T: "potrójna pionowa kreska z prawym znacznikiem dedukcji"] # 0x22aa + - "⊫": [T: "podwójna pionowa kreska z podwójnym prawym znacznikiem dedukcji"] # 0x22ab + - "⊬": [T: "nie dowodzi"] # 0x22ac - "⊭": # 0x22ad - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "nie jest prawdziwe" - - "⊮": [t: "nie wymusza"] # 0x22ae - - "⊯": [t: "zanegowana podwójna pionowa kreska z podwójnym prawym znacznikiem dedukcji"] # 0x22af - - "⊰": [t: "poprzedza pod relacją"] # 0x22b0 - - "⊱": [t: "następuje po pod relacją"] # 0x22b1 + - T: "nie jest prawdziwe" + - "⊮": [T: "nie wymusza"] # 0x22ae + - "⊯": [T: "zanegowana podwójna pionowa kreska z podwójnym prawym znacznikiem dedukcji"] # 0x22af + - "⊰": [T: "poprzedza pod relacją"] # 0x22b0 + - "⊱": [T: "następuje po pod relacją"] # 0x22b1 - "⊲": # 0x22b2 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "normalną podgrupą" - - "⊳": [t: "zawiera jako normalną podgrupę"] # 0x22b3 + - T: "normalną podgrupą" + - "⊳": [T: "zawiera jako normalną podgrupę"] # 0x22b3 - "⊴": # 0x22b4 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "normalną podgrupą lub równe" - - "⊵": [t: "zawiera jako normalną podgrupę lub jest równe"] # 0x22b5 + - T: "normalną podgrupą lub równe" + - "⊵": [T: "zawiera jako normalną podgrupę lub jest równe"] # 0x22b5 - "⊶": # 0x22b6 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "oryginałem" + - T: "oryginałem" - "⊷": # 0x22b7 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "obrazem" - - "⊸": [t: "multimapa"] # 0x22b8 - - "⊹": [t: "macierz sprzężona hermitowsko"] # 0x22b9 - - "⊺": [t: "interkalacja"] # 0x22ba - - "⊻": [t: "alternatywa wykluczająca"] # 0x22bb - - "⊼": [t: "negacja koniunkcji"] # 0x22bc - - "⊽": [t: "negacja alternatywy"] # 0x22bd - - "⊾": [t: "kąt prosty z łukiem"] # 0x22be - - "⊿": [t: "prawy trójkąt"] # 0x22bf - - "⋀": [t: "logiczne I"] # 0x22c0 - - "⋁": [t: "logiczne lub"] # 0x22c1 + - T: "obrazem" + - "⊸": [T: "multimapa"] # 0x22b8 + - "⊹": [T: "macierz sprzężona hermitowsko"] # 0x22b9 + - "⊺": [T: "interkalacja"] # 0x22ba + - "⊻": [T: "alternatywa wykluczająca"] # 0x22bb + - "⊼": [T: "negacja koniunkcji"] # 0x22bc + - "⊽": [T: "negacja alternatywy"] # 0x22bd + - "⊾": [T: "kąt prosty z łukiem"] # 0x22be + - "⊿": [T: "prawy trójkąt"] # 0x22bf + - "⋀": [T: "logiczne I"] # 0x22c0 + - "⋁": [T: "logiczne lub"] # 0x22c1 - "⋂": [T: "przecięcie"] # 0x22c2 - "⋃": [T: "suma zbiorów"] # 0x22c3 - - "⋄": [t: "operator rombu"] # 0x22c4 + - "⋄": [T: "operator rombu"] # 0x22c4 - "⋅": # 0x22c5 - test: if: "@data-chem-formula-op" @@ -1143,520 +1143,520 @@ else: [T: "razy"] - "⋆": [T: "razy"] # 0x22c6 - - "⋇": [t: "znak dzielenia razy"] # 0x22c7 - - "⋈": [t: "muszka"] # 0x22c8 + - "⋇": [T: "znak dzielenia razy"] # 0x22c7 + - "⋈": [T: "muszka"] # 0x22c8 - "⋉": # 0x22c9 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "lewym iloczynem półprostym z czynnikiem normalnym" + - T: "lewym iloczynem półprostym z czynnikiem normalnym" - "⋊": # 0x22ca - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "prawym iloczynem półprostym z czynnikiem normalnym" + - T: "prawym iloczynem półprostym z czynnikiem normalnym" - "⋋": # 0x22cb - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "lewym iloczynem półprostym" + - T: "lewym iloczynem półprostym" - "⋌": # 0x22cc - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "prawym iloczynem półprostym" - - "⋍": [t: "odwrócona tylda równa się"] # 0x22cd - - "⋎": [t: "zakręcone logiczne lub"] # 0x22ce - - "⋏": [t: "zakręcone logiczne i"] # 0x22cf + - T: "prawym iloczynem półprostym" + - "⋍": [T: "odwrócona tylda równa się"] # 0x22cd + - "⋎": [T: "zakręcone logiczne lub"] # 0x22ce + - "⋏": [T: "zakręcone logiczne i"] # 0x22cf - "⋐": # 0x22d0 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "podwójnym podzbiorem" + - T: "podwójnym podzbiorem" - "⋑": # 0x22d1 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "podwójnym nadzbiorem" + - T: "podwójnym nadzbiorem" - "⋒": # 0x22d2 - - t: "podwójna część wspólna" + - T: "podwójna część wspólna" - "⋓": # 0x22d3 - - t: "podwójna suma zbiorów" + - T: "podwójna suma zbiorów" - "⋔": # 0x22d4 - - t: "właściwa część wspólna" + - T: "właściwa część wspólna" - "⋕": # 0x22d5 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "równe i równoległe do" - - "⋖": [t: "mniejsze niż z kropką"] # 0x22d6 - - "⋗": [t: "większe niż z kropką"] # 0x22d7 + - T: "równe i równoległe do" + - "⋖": [T: "mniejsze niż z kropką"] # 0x22d6 + - "⋗": [T: "większe niż z kropką"] # 0x22d7 - "⋘": # 0x22d8 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "bardzo znacznie mniejsze niż" + - T: "bardzo znacznie mniejsze niż" - "⋙": # 0x22d9 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "bardzo znacznie większe niż" + - T: "bardzo znacznie większe niż" - "⋚": # 0x22da - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "mniejsze niż, równe lub większe niż" + - T: "mniejsze niż, równe lub większe niż" - "⋛": # 0x22db - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "większe niż, równe lub mniejsze niż" + - T: "większe niż, równe lub mniejsze niż" - "⋜": # 0x22dc - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "równe lub mniejsze niż" + - T: "równe lub mniejsze niż" - "⋝": # 0x22dd - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "równe lub większe niż" + - T: "równe lub większe niż" - "⋞": # 0x22de - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "równe lub poprzedza" + - T: "równe lub poprzedza" - "⋟": # 0x22df - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "równe lub następuje po" - - "⋠": [t: "nie poprzedza ani nie jest równe"] # 0x22e0 - - "⋡": [t: "nie następuje po ani nie jest równe"] # 0x22e1 - - "⋢": [t: "nie poprzedza kwadratowo ani nie jest równe"] # 0x22e2 - - "⋣": [t: "nie następuje kwadratowo po ani nie jest równe"] # 0x22e3 - - "⋤": [t: "poprzedza kwadratowo, ale nie jest równe"] # 0x22e4 - - "⋥": [t: "następuje kwadratowo po, ale nie jest równe"] # 0x22e5 + - T: "równe lub następuje po" + - "⋠": [T: "nie poprzedza ani nie jest równe"] # 0x22e0 + - "⋡": [T: "nie następuje po ani nie jest równe"] # 0x22e1 + - "⋢": [T: "nie poprzedza kwadratowo ani nie jest równe"] # 0x22e2 + - "⋣": [T: "nie następuje kwadratowo po ani nie jest równe"] # 0x22e3 + - "⋤": [T: "poprzedza kwadratowo, ale nie jest równe"] # 0x22e4 + - "⋥": [T: "następuje kwadratowo po, ale nie jest równe"] # 0x22e5 - "⋦": # 0x22e6 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "mniejsze niż, ale nie równoważne" + - T: "mniejsze niż, ale nie równoważne" - "⋧": # 0x22e7 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "większe niż, ale nie równoważne" - - "⋨": [t: "poprzedza, ale nie jest równoważne"] # 0x22e8 - - "⋩": [t: "następuje po, ale nie jest równoważne"] # 0x22e9 + - T: "większe niż, ale nie równoważne" + - "⋨": [T: "poprzedza, ale nie jest równoważne"] # 0x22e8 + - "⋩": [T: "następuje po, ale nie jest równoważne"] # 0x22e9 - "⋪": # 0x22ea - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "nie jest normalną podgrupą" - - "⋫": [t: "nie zawiera jako normalnej podgrupy"] # 0x22eb + - T: "nie jest normalną podgrupą" + - "⋫": [T: "nie zawiera jako normalnej podgrupy"] # 0x22eb - "⋬": # 0x22ec - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "nie jest normalną podgrupą ani równe" - - "⋭": [t: "nie zawiera jako normalnej podgrupy ani równe"] # 0x22ed - - "⋮": [t: "pionowy wielokropek"] # 0x22ee - - "⋯": [t: "trzy kropki"] # 0x22ef - - "⋰": [t: "ukośny wielokropek w górę"] # 0x22f0 - - "⋱": [t: "ukośny wielokropek w dół"] # 0x22f1 - - "⋲": [t: "należy do z długą poziomą kreską"] # 0x22f2 - - "⋳": [t: "należy do z pionową kreską na końcu i poziomą kreską"] # 0x22f3 - - "⋴": [t: "należy do z pionową kreską na końcu i poziomą kreską"] # 0x22f4 - - "⋵": [t: "należy do z kropką nad"] # 0x22f5 - - "⋶": [t: "należy do z kreską nad"] # 0x22f6 - - "⋷": [t: "należy do z kreską nad"] # 0x22f7 - - "⋸": [t: "należy do z podkreśleniem"] # 0x22f8 - - "⋹": [t: "należy do z dwiema poziomymi kreskami"] # 0x22f9 - - "⋺": [t: "zawiera z długą poziomą kreską"] # 0x22fa - - "⋻": [t: "zawiera z pionową kreską na końcu i poziomą kreską"] # 0x22fb - - "⋼": [t: "zawiera z pionową kreską na końcu i poziomą kreską"] # 0x22fc - - "⋽": [t: "zawiera z kreską nad"] # 0x22fd - - "⋾": [t: "zawiera z kreską nad"] # 0x22fe - - "⋿": [t: "przynależność do wielozbioru w notacji Z"] # 0x22ff - - "⌀": [t: "znak średnicy"] # 0x2300 - - "⌁": [t: "strzałka elektryczna"] # 0x2301 - - "⌂": [t: "dom"] # 0x2302 - - "⌃": [t: "górny grot strzałki"] # 0x2303 - - "⌄": [t: "dolny grot strzałki"] # 0x2304 - - "⌅": [t: "rzutowy"] # 0x2305 - - "⌆": [t: "perspektywiczny"] # 0x2306 - - "⌇": [t: "linia falista"] # 0x2307 - - "⌈": [t: "lewy sufit"] # 0x2308 - - "⌉": [t: "prawy sufit"] # 0x2309 - - "⌊": [t: "lewa podłoga"] # 0x230a - - "⌋": [t: "prawa podłoga"] # 0x230b - - "⌌": [t: "dolny prawy znacznik kadrowania"] # 0x230c - - "⌍": [t: "dolny lewy znacznik kadrowania"] # 0x230d - - "⌎": [t: "górny prawy znacznik kadrowania"] # 0x230e - - "⌏": [t: "górny lewy znacznik kadrowania"] # 0x230f - - "⌐": [t: "odwrócony nie znak"] # 0x2310 - - "⌑": [t: "kwadrat romb"] # 0x2311 + - T: "nie jest normalną podgrupą ani równe" + - "⋭": [T: "nie zawiera jako normalnej podgrupy ani równe"] # 0x22ed + - "⋮": [T: "pionowy wielokropek"] # 0x22ee + - "⋯": [T: "trzy kropki"] # 0x22ef + - "⋰": [T: "ukośny wielokropek w górę"] # 0x22f0 + - "⋱": [T: "ukośny wielokropek w dół"] # 0x22f1 + - "⋲": [T: "należy do z długą poziomą kreską"] # 0x22f2 + - "⋳": [T: "należy do z pionową kreską na końcu i poziomą kreską"] # 0x22f3 + - "⋴": [T: "należy do z pionową kreską na końcu i poziomą kreską"] # 0x22f4 + - "⋵": [T: "należy do z kropką nad"] # 0x22f5 + - "⋶": [T: "należy do z kreską nad"] # 0x22f6 + - "⋷": [T: "należy do z kreską nad"] # 0x22f7 + - "⋸": [T: "należy do z podkreśleniem"] # 0x22f8 + - "⋹": [T: "należy do z dwiema poziomymi kreskami"] # 0x22f9 + - "⋺": [T: "zawiera z długą poziomą kreską"] # 0x22fa + - "⋻": [T: "zawiera z pionową kreską na końcu i poziomą kreską"] # 0x22fb + - "⋼": [T: "zawiera z pionową kreską na końcu i poziomą kreską"] # 0x22fc + - "⋽": [T: "zawiera z kreską nad"] # 0x22fd + - "⋾": [T: "zawiera z kreską nad"] # 0x22fe + - "⋿": [T: "przynależność do wielozbioru w notacji Z"] # 0x22ff + - "⌀": [T: "znak średnicy"] # 0x2300 + - "⌁": [T: "strzałka elektryczna"] # 0x2301 + - "⌂": [T: "dom"] # 0x2302 + - "⌃": [T: "górny grot strzałki"] # 0x2303 + - "⌄": [T: "dolny grot strzałki"] # 0x2304 + - "⌅": [T: "rzutowy"] # 0x2305 + - "⌆": [T: "perspektywiczny"] # 0x2306 + - "⌇": [T: "linia falista"] # 0x2307 + - "⌈": [T: "lewy sufit"] # 0x2308 + - "⌉": [T: "prawy sufit"] # 0x2309 + - "⌊": [T: "lewa podłoga"] # 0x230a + - "⌋": [T: "prawa podłoga"] # 0x230b + - "⌌": [T: "dolny prawy znacznik kadrowania"] # 0x230c + - "⌍": [T: "dolny lewy znacznik kadrowania"] # 0x230d + - "⌎": [T: "górny prawy znacznik kadrowania"] # 0x230e + - "⌏": [T: "górny lewy znacznik kadrowania"] # 0x230f + - "⌐": [T: "odwrócony nie znak"] # 0x2310 + - "⌑": [T: "kwadrat romb"] # 0x2311 - "⌒": [T: "łuk"] # 0x2312 - - "⌓": [t: "odcinek"] # 0x2313 - - "⌔": [t: "sektor"] # 0x2314 - - "⌕": [t: "rejestrator telefoniczny"] # 0x2315 - - "⌖": [t: "celownik wskaźnika pozycji"] # 0x2316 - - "⌗": [t: "kwadrat danych ekranowych"] # 0x2317 - - "⌘": [t: "znak miejsca zainteresowania"] # 0x2318 - - "⌙": [t: "odwrócony nie znak"] # 0x2319 - - "⌚": [t: "zegarek"] # 0x231a - - "⌛": [t: "klepsydra"] # 0x231b - - "⌜": [t: "górny lewy narożnik"] # 0x231c - - "⌝": [t: "górny prawy narożnik"] # 0x231d - - "⌞": [t: "dolny lewy narożnik"] # 0x231e - - "⌟": [t: "dolny prawy narożnik"] # 0x231f - - "⌠": [t: "górna połowa całki"] # 0x2320 - - "⌡": [t: "dolna połowa całki"] # 0x2321 - - "⌢": [t: "łuk w dół"] # 0x2322 - - "⌣": [t: "łuk w górę"] # 0x2323 - - "⌤": [t: "górny grot strzałki między dwiema poziomymi kreskami"] # 0x2324 - - "⌥": [t: "klawisz opcji"] # 0x2325 - - "⌦": [t: "wymaż w prawo"] # 0x2326 - - "⌧": [t: "X w prostokątnej ramce"] # 0x2327 - - "⌨": [t: "klawiatura"] # 0x2328 - - "〈": [t: "kąt skierowany w lewo nawias kwadratowy"] # 0x2329 - - "〉": [t: "kąt skierowany w prawo nawias kwadratowy"] # 0x232a - - "⌫": [t: "wymaż w lewo"] # 0x232b - - "⌬": [t: "pierścień benzenowy"] # 0x232c - - "⌭": [t: "walcowość"] # 0x232d - - "⌮": [t: "profil dookoła"] # 0x232e - - "⌯": [t: "symetria"] # 0x232f - - "⌰": [t: "bicie całkowite"] # 0x2330 - - "⌱": [t: "początek wymiarowania"] # 0x2331 - - "⌲": [t: "stożkowatość"] # 0x2332 - - "⌳": [t: "nachylenie"] # 0x2333 - - "⌴": [t: "pogłębienie walcowe"] # 0x2334 - - "⌵": [t: "pogłębienie stożkowe"] # 0x2335 - - "⌶": [t: "belka i APL"] # 0x2336 - - "⌽": [t: "okrąg z kreską pionową APL"] # 0x233d - - "⌿": [t: "apl ukośnik kreska"] # 0x233f - - "⍰": [t: "nieznana ramka"] # 0x2370 - - "⍼": [t: "kąt prosty z dolną strzałką zygzakowatą"] # 0x237c - - "⎔": [t: "sześciokąt"] # 0x2394 - - "⎕": [t: "ramka"] # 0x2395 - - "⎶": [t: "dolny nawias kwadratowy nad górnym nawiasem kwadratowym"] # 0x23b6 - - "⏜": [t: "górny nawias"] # 0x23dc - - "⏝": [t: "dolny nawias"] # 0x23dd - - "⏞": [t: "górny nawias klamrowy"] # 0x23de - - "⏟": [t: "dolny nawias klamrowy"] # 0x23df - - "⏠": [t: "górny nawias skorupowy"] # 0x23e0 - - "⏡": [t: "dolny nawias skorupowy"] # 0x23e1 - - "⏢": [t: "biały trapez"] # 0x23e2 - - "⏣": [t: "benzenowy pierścień z okręgiem po prawej"] # 0x23e3 - - "⏤": [t: "prostoliniowość"] # 0x23e4 - - "⏥": [t: "płaskość"] # 0x23e5 - - "⏦": [t: "prąd przemienny"] # 0x23e6 - - "⏧": [t: "symbol elektrycznego przecięcia"] # 0x23e7 + - "⌓": [T: "odcinek"] # 0x2313 + - "⌔": [T: "sektor"] # 0x2314 + - "⌕": [T: "rejestrator telefoniczny"] # 0x2315 + - "⌖": [T: "celownik wskaźnika pozycji"] # 0x2316 + - "⌗": [T: "kwadrat danych ekranowych"] # 0x2317 + - "⌘": [T: "znak miejsca zainteresowania"] # 0x2318 + - "⌙": [T: "odwrócony nie znak"] # 0x2319 + - "⌚": [T: "zegarek"] # 0x231a + - "⌛": [T: "klepsydra"] # 0x231b + - "⌜": [T: "górny lewy narożnik"] # 0x231c + - "⌝": [T: "górny prawy narożnik"] # 0x231d + - "⌞": [T: "dolny lewy narożnik"] # 0x231e + - "⌟": [T: "dolny prawy narożnik"] # 0x231f + - "⌠": [T: "górna połowa całki"] # 0x2320 + - "⌡": [T: "dolna połowa całki"] # 0x2321 + - "⌢": [T: "łuk w dół"] # 0x2322 + - "⌣": [T: "łuk w górę"] # 0x2323 + - "⌤": [T: "górny grot strzałki między dwiema poziomymi kreskami"] # 0x2324 + - "⌥": [T: "klawisz opcji"] # 0x2325 + - "⌦": [T: "wymaż w prawo"] # 0x2326 + - "⌧": [T: "X w prostokątnej ramce"] # 0x2327 + - "⌨": [T: "klawiatura"] # 0x2328 + - "〈": [T: "kąt skierowany w lewo nawias kwadratowy"] # 0x2329 + - "〉": [T: "kąt skierowany w prawo nawias kwadratowy"] # 0x232a + - "⌫": [T: "wymaż w lewo"] # 0x232b + - "⌬": [T: "pierścień benzenowy"] # 0x232c + - "⌭": [T: "walcowość"] # 0x232d + - "⌮": [T: "profil dookoła"] # 0x232e + - "⌯": [T: "symetria"] # 0x232f + - "⌰": [T: "bicie całkowite"] # 0x2330 + - "⌱": [T: "początek wymiarowania"] # 0x2331 + - "⌲": [T: "stożkowatość"] # 0x2332 + - "⌳": [T: "nachylenie"] # 0x2333 + - "⌴": [T: "pogłębienie walcowe"] # 0x2334 + - "⌵": [T: "pogłębienie stożkowe"] # 0x2335 + - "⌶": [T: "belka i APL"] # 0x2336 + - "⌽": [T: "okrąg z kreską pionową APL"] # 0x233d + - "⌿": [T: "apl ukośnik kreska"] # 0x233f + - "⍰": [T: "nieznana ramka"] # 0x2370 + - "⍼": [T: "kąt prosty z dolną strzałką zygzakowatą"] # 0x237c + - "⎔": [T: "sześciokąt"] # 0x2394 + - "⎕": [T: "ramka"] # 0x2395 + - "⎶": [T: "dolny nawias kwadratowy nad górnym nawiasem kwadratowym"] # 0x23b6 + - "⏜": [T: "górny nawias"] # 0x23dc + - "⏝": [T: "dolny nawias"] # 0x23dd + - "⏞": [T: "górny nawias klamrowy"] # 0x23de + - "⏟": [T: "dolny nawias klamrowy"] # 0x23df + - "⏠": [T: "górny nawias skorupowy"] # 0x23e0 + - "⏡": [T: "dolny nawias skorupowy"] # 0x23e1 + - "⏢": [T: "biały trapez"] # 0x23e2 + - "⏣": [T: "benzenowy pierścień z okręgiem po prawej"] # 0x23e3 + - "⏤": [T: "prostoliniowość"] # 0x23e4 + - "⏥": [T: "płaskość"] # 0x23e5 + - "⏦": [T: "prąd przemienny"] # 0x23e6 + - "⏧": [T: "symbol elektrycznego przecięcia"] # 0x23e7 - "①-⑨": # 0x2460 - 0x2469 - - t: "w kółku" + - T: "w kółku" - spell: "translate('.', '①②③④⑤⑥⑦⑧⑨', '123456789')" - - "⑩": [t: "w kółku dziesięć"] # 0x2469 - - "⑪": [t: "w kółku jedenaście"] # 0x246a - - "⑫": [t: "w kółku dwanaście"] # 0x246b - - "⑬": [t: "w kółku trzynaście"] # 0x246c - - "⑭": [t: "w kółku czternaście"] # 0x246d - - "⑮": [t: "w kółku piętnaście"] # 0x246e - - "⑯": [t: "w kółku szesnaście"] # 0x246f - - "⑰": [t: "w kółku siedemnaście"] # 0x2470 - - "⑱": [t: "w kółku osiemnaście"] # 0x2471 - - "⑳": [t: "w kółku dwadzieścia"] # 0x2473 + - "⑩": [T: "w kółku dziesięć"] # 0x2469 + - "⑪": [T: "w kółku jedenaście"] # 0x246a + - "⑫": [T: "w kółku dwanaście"] # 0x246b + - "⑬": [T: "w kółku trzynaście"] # 0x246c + - "⑭": [T: "w kółku czternaście"] # 0x246d + - "⑮": [T: "w kółku piętnaście"] # 0x246e + - "⑯": [T: "w kółku szesnaście"] # 0x246f + - "⑰": [T: "w kółku siedemnaście"] # 0x2470 + - "⑱": [T: "w kółku osiemnaście"] # 0x2471 + - "⑳": [T: "w kółku dwadzieścia"] # 0x2473 - "⑴-⑼": # 0x2474 - 0x247d - - t: "w nawiasie" + - T: "w nawiasie" - spell: "translate('.', '⑴⑵⑶⑷⑸⑹⑺⑻⑼', '123456789')" - - "⑽": [t: "w nawiasie dziesięć"] # 0x247d - - "⑾": [t: "w nawiasie jedenaście"] # 0x247e - - "⑿": [t: "w nawiasie dwanaście"] # 0x247f - - "⒀": [t: "w nawiasie trzynaście"] # 0x2480 - - "⒁": [t: "w nawiasie czternaście"] # 0x2481 - - "⒂": [t: "w nawiasie piętnaście"] # 0x2482 - - "⒃": [t: "w nawiasie szesnaście"] # 0x2483 - - "⒄": [t: "w nawiasie siedemnaście"] # 0x2484 - - "⒅": [t: "w nawiasie osiemnaście"] # 0x2485 - - "⒆": [t: "w nawiasie dziewiętnaście"] # 0x2486 - - "⒇": [t: "w nawiasie dwadzieścia"] # 0x2487 + - "⑽": [T: "w nawiasie dziesięć"] # 0x247d + - "⑾": [T: "w nawiasie jedenaście"] # 0x247e + - "⑿": [T: "w nawiasie dwanaście"] # 0x247f + - "⒀": [T: "w nawiasie trzynaście"] # 0x2480 + - "⒁": [T: "w nawiasie czternaście"] # 0x2481 + - "⒂": [T: "w nawiasie piętnaście"] # 0x2482 + - "⒃": [T: "w nawiasie szesnaście"] # 0x2483 + - "⒄": [T: "w nawiasie siedemnaście"] # 0x2484 + - "⒅": [T: "w nawiasie osiemnaście"] # 0x2485 + - "⒆": [T: "w nawiasie dziewiętnaście"] # 0x2486 + - "⒇": [T: "w nawiasie dwadzieścia"] # 0x2487 - "⒈-⒐": # 0x2488 - 0x2491 - spell: "translate('.', '⒈⒉⒊⒋⒌⒍⒎⒏⒐', '123456789')" - - t: "z kropką" - - "⒑": [t: "dziesięć z kropką"] # 0x2491 - - "⒒": [t: "jedenaście z kropką"] # 0x2492 - - "⒓": [t: "dwanaście z kropką"] # 0x2493 - - "⒔": [t: "trzynaście z kropką"] # 0x2494 - - "⒕": [t: "czternaście z kropką"] # 0x2495 - - "⒖": [t: "piętnaście z kropką"] # 0x2496 - - "⒗": [t: "szesnaście z kropką"] # 0x2497 - - "⒘": [t: "siedemnaście z kropką"] # 0x2498 - - "⒙": [t: "osiemnaście z kropką"] # 0x2499 - - "⒚": [t: "dziewiętnaście z kropką"] # 0x249a - - "⒛": [t: "dwadzieścia z kropką"] # 0x249b + - T: "z kropką" + - "⒑": [T: "dziesięć z kropką"] # 0x2491 + - "⒒": [T: "jedenaście z kropką"] # 0x2492 + - "⒓": [T: "dwanaście z kropką"] # 0x2493 + - "⒔": [T: "trzynaście z kropką"] # 0x2494 + - "⒕": [T: "czternaście z kropką"] # 0x2495 + - "⒖": [T: "piętnaście z kropką"] # 0x2496 + - "⒗": [T: "szesnaście z kropką"] # 0x2497 + - "⒘": [T: "siedemnaście z kropką"] # 0x2498 + - "⒙": [T: "osiemnaście z kropką"] # 0x2499 + - "⒚": [T: "dziewiętnaście z kropką"] # 0x249a + - "⒛": [T: "dwadzieścia z kropką"] # 0x249b - "⒜-⒵": # 0x249c - 0x24b5 - - t: "w nawiasie" + - T: "w nawiasie" - spell: "translate('.', '⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵', 'abcdefghijklmnopqrstuvwxyz')" - "Ⓐ-ⓩ": # 0x24b6 - 0x24cf - - t: "w kółku" + - T: "w kółku" - spell: "translate('.', 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "🅐-🅩": # 0x1f150 - 0x1f169 - - t: "czarny w kółku" + - T: "czarny w kółku" - spell: "translate('.', '🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "ⓐ-ⓩ": # 0x24d0 - 0x24e9 - - t: "w kółku" + - T: "w kółku" - spell: "translate('.', 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ', 'abcdefghijklmnopqrstuvwxyz')" - - "⓪": [t: "w kółku zero"] # 0x24ea - - "⓫": [t: "czarny w kółku jedenaście"] # 0x24eb - - "⓬": [t: "czarny w kółku dwanaście"] # 0x24ec - - "⓭": [t: "czarny w kółku trzynaście"] # 0x24ed - - "⓮": [t: "czarny w kółku czternaście"] # 0x24ee - - "⓯": [t: "czarny w kółku piętnaście"] # 0x24ef - - "⓰": [t: "czarny w kółku szesnaście"] # 0x24f0 - - "⓱": [t: "czarny w kółku siedemnaście"] # 0x24f1 - - "⓲": [t: "czarny w kółku osiemnaście"] # 0x24f2 - - "⓳": [t: "czarny w kółku dziewiętnaście"] # 0x24f3 - - "⓴": [t: "czarny w kółku dwadzieścia"] # 0x24f4 + - "⓪": [T: "w kółku zero"] # 0x24ea + - "⓫": [T: "czarny w kółku jedenaście"] # 0x24eb + - "⓬": [T: "czarny w kółku dwanaście"] # 0x24ec + - "⓭": [T: "czarny w kółku trzynaście"] # 0x24ed + - "⓮": [T: "czarny w kółku czternaście"] # 0x24ee + - "⓯": [T: "czarny w kółku piętnaście"] # 0x24ef + - "⓰": [T: "czarny w kółku szesnaście"] # 0x24f0 + - "⓱": [T: "czarny w kółku siedemnaście"] # 0x24f1 + - "⓲": [T: "czarny w kółku osiemnaście"] # 0x24f2 + - "⓳": [T: "czarny w kółku dziewiętnaście"] # 0x24f3 + - "⓴": [T: "czarny w kółku dwadzieścia"] # 0x24f4 - "⓵-⓽": # 0x24f5 - 0x24fe - - t: "podwójny w kółku" + - T: "podwójny w kółku" - spell: "translate('.', '⓵⓶⓷⓸⓹⓺⓻⓼⓽', '123456789')" - - "⓾": [t: "podwójny w kółku dziesięć"] # 0x24fe - - "⓿": [t: "czarny w kółku zero"] # 0x24ff - - "■": [t: "czarny kwadrat"] # 0x25a0 - - "□": [t: "biały kwadrat"] # 0x25a1 - - "▢": [t: "biały kwadrat z zaokrąglonymi narożnikami"] # 0x25a2 - - "▣": [t: "biały kwadrat zawierający mały czarny kwadrat"] # 0x25a3 - - "▤": [t: "kwadrat z poziomym wypełnieniem"] # 0x25a4 - - "▥": [t: "kwadrat z pionowym wypełnieniem"] # 0x25a5 - - "▦": [t: "kwadrat z ortogonalnym kreskowanym wypełnieniem"] # 0x25a6 - - "▧": [t: "kwadrat z wypełnieniem od góry po lewej do dołu po prawej"] # 0x25a7 - - "▨": [t: "kwadrat z wypełnieniem od góry po prawej do dołu po lewej"] # 0x25a8 - - "▩": [t: "kwadrat z ukośnym kreskowanym wypełnieniem"] # 0x25a9 - - "▪": [t: "czarny mały kwadrat"] # 0x25aa - - "▫": [t: "biały mały kwadrat"] # 0x25ab - - "▬": [t: "czarny prostokąt"] # 0x25ac - - "▭": [t: "biały prostokąt"] # 0x25ad - - "▮": [t: "czarny pionowy prostokąt"] # 0x25ae - - "▯": [t: "biały pionowy prostokąt"] # 0x25af - - "▰": [t: "czarny Równoległobok"] # 0x25b0 - - "▱": [t: "biały Równoległobok"] # 0x25b1 - - "▲": [t: "czarny trójkąt skierowany w górę"] # 0x25b2 - - "△": [t: "trójkąt"] # 0x25b3 - - "▴": [t: "czarny mały trójkąt skierowany w górę"] # 0x25b4 - - "▵": [t: "biały mały trójkąt skierowany w górę"] # 0x25b5 - - "▶": [t: "czarny trójkąt skierowany w prawo"] # 0x25b6 - - "▷": [t: "biały trójkąt skierowany w prawo"] # 0x25b7 - - "▸": [t: "czarny mały trójkąt skierowany w prawo"] # 0x25b8 - - "▹": [t: "biały mały trójkąt skierowany w prawo"] # 0x25b9 - - "►": [t: "czarny wskaźnik skierowany w prawo"] # 0x25ba - - "▻": [t: "biały wskaźnik skierowany w prawo"] # 0x25bb - - "▼": [t: "czarny trójkąt skierowany w dół"] # 0x25bc - - "▽": [t: "biały trójkąt skierowany w dół"] # 0x25bd - - "▾": [t: "czarny mały trójkąt skierowany w dół"] # 0x25be - - "▿": [t: "biały mały trójkąt skierowany w dół"] # 0x25bf - - "◀": [t: "czarny trójkąt skierowany w lewo"] # 0x25c0 - - "◁": [t: "biały trójkąt skierowany w lewo"] # 0x25c1 - - "◂": [t: "czarny mały trójkąt skierowany w lewo"] # 0x25c2 - - "◃": [t: "biały mały trójkąt skierowany w lewo"] # 0x25c3 - - "◄": [t: "czarny wskaźnik skierowany w lewo"] # 0x25c4 - - "◅": [t: "biały wskaźnik skierowany w lewo"] # 0x25c5 - - "◆": [t: "czarny romb"] # 0x25c6 - - "◇": [t: "biały romb"] # 0x25c7 - - "◈": [t: "biały romb zawierający czarny mały romb"] # 0x25c8 - - "◉": [t: "rybie oko"] # 0x25c9 - - "◊": [t: "romb"] # 0x25ca - - "○": [t: "białe koło"] # 0x25cb - - "◌": [t: "kropkowany okrąg"] # 0x25cc - - "◍": [t: "okrąg z pionowe wypełnienie"] # 0x25cd - - "◎": [t: "tarcza"] # 0x25ce - - "●": [t: "czarne koło"] # 0x25cf - - "◐": [t: "okrąg z czarną lewą połową"] # 0x25d0 - - "◑": [t: "okrąg z czarną prawą połową"] # 0x25d1 - - "◒": [t: "okrąg z czarną dolną połową"] # 0x25d2 - - "◓": [t: "okrąg z czarną górną połową"] # 0x25d3 - - "◔": [t: "okrąg z czarną górną prawą ćwiartką"] # 0x25d4 - - "◕": [t: "okrąg czarny poza górną lewą ćwiartką"] # 0x25d5 - - "◖": [t: "czarne koło z lewą połową"] # 0x25d6 - - "◗": [t: "czarne koło z prawą połową"] # 0x25d7 - - "◘": [t: "odwrotność punkt"] # 0x25d8 - - "◙": [t: "odwrotność białe koło"] # 0x25d9 - - "◚": [t: "górna połowa odwrotność białe koło"] # 0x25da - - "◛": [t: "dolna połowa odwrotność białe koło"] # 0x25db - - "◜": [t: "górny lewy ćwiartka łuk kołowy"] # 0x25dc - - "◝": [t: "górny prawy ćwiartka łuk kołowy"] # 0x25dd - - "◞": [t: "dolny prawy ćwiartka łuk kołowy"] # 0x25de - - "◟": [t: "dolny lewy ćwiartka łuk kołowy"] # 0x25df - - "◠": [t: "górna połowa okrąg"] # 0x25e0 - - "◡": [t: "dolna połowa okrąg"] # 0x25e1 - - "◢": [t: "czarny dolny prawy trójkąt"] # 0x25e2 - - "◣": [t: "czarny dolny lewy trójkąt"] # 0x25e3 - - "◤": [t: "czarny górny lewy trójkąt"] # 0x25e4 - - "◥": [t: "czarny górny prawy trójkąt"] # 0x25e5 - - "◦": [t: "złożenie"] # 0x25e6 - - "◧": [t: "kwadrat z czarną lewą połową"] # 0x25e7 - - "◨": [t: "kwadrat z czarną prawą połową"] # 0x25e8 - - "◩": [t: "kwadrat z czarną górną lewą połową"] # 0x25e9 - - "◪": [t: "kwadrat z czarną dolną prawą połową"] # 0x25ea - - "◫": [t: "biały kwadrat z linią dzielącą na pół"] # 0x25eb - - "◬": [t: "biały trójkąt skierowany w górę z kropką"] # 0x25ec - - "◭": [t: "trójkąt skierowany w górę z czarną lewą połową"] # 0x25ed - - "◮": [t: "trójkąt skierowany w górę z czarną prawą połową"] # 0x25ee - - "◯": [t: "duży okrąg"] # 0x25ef - - "◰": [t: "biały kwadrat z górną lewą ćwiartką"] # 0x25f0 - - "◱": [t: "biały kwadrat z dolną lewą ćwiartką"] # 0x25f1 - - "◲": [t: "biały kwadrat z dolną prawą ćwiartką"] # 0x25f2 - - "◳": [t: "biały kwadrat z górną prawą ćwiartką"] # 0x25f3 - - "◴": [t: "białe koło z górną lewą ćwiartką"] # 0x25f4 - - "◵": [t: "białe koło z dolną lewą ćwiartką"] # 0x25f5 - - "◶": [t: "białe koło z dolną prawą ćwiartką"] # 0x25f6 - - "◷": [t: "białe koło z górną prawą ćwiartką"] # 0x25f7 - - "◸": [t: "górny lewy trójkąt"] # 0x25f8 - - "◹": [t: "górny prawy trójkąt"] # 0x25f9 - - "◺": [t: "dolny lewy trójkąt"] # 0x25fa - - "◻": [t: "biały średni kwadrat"] # 0x25fb - - "◼": [t: "czarny średni kwadrat"] # 0x25fc - - "◽": [t: "biały średni mały kwadrat"] # 0x25fd - - "◾": [t: "czarny średni mały kwadrat"] # 0x25fe - - "◿": [t: "dolny prawy trójkąt"] # 0x25ff - - "★": [t: "czarna gwiazda"] # 0x2605 - - "☆": [t: "biały gwiazda"] # 0x2606 - - "☉": [t: "słońce"] # 0x2609 - - "☌": [t: "koniunkcja"] # 0x260c - - "☒": [t: "ramka wyboru z iksem"] # 0x2612 - - "☽": [t: "przybywający księżyc"] # 0x263d - - "☾": [t: "ubywający księżyc"] # 0x263e - - "☿": [t: "Merkury"] # 0x263f - - "♀": [t: "żeńskie"] # 0x2640 - - "♁": [t: "Ziemia"] # 0x2641 - - "♂": [t: "męskie"] # 0x2642 - - "♃": [t: "Jowisz"] # 0x2643 + - "⓾": [T: "podwójny w kółku dziesięć"] # 0x24fe + - "⓿": [T: "czarny w kółku zero"] # 0x24ff + - "■": [T: "czarny kwadrat"] # 0x25a0 + - "□": [T: "biały kwadrat"] # 0x25a1 + - "▢": [T: "biały kwadrat z zaokrąglonymi narożnikami"] # 0x25a2 + - "▣": [T: "biały kwadrat zawierający mały czarny kwadrat"] # 0x25a3 + - "▤": [T: "kwadrat z poziomym wypełnieniem"] # 0x25a4 + - "▥": [T: "kwadrat z pionowym wypełnieniem"] # 0x25a5 + - "▦": [T: "kwadrat z ortogonalnym kreskowanym wypełnieniem"] # 0x25a6 + - "▧": [T: "kwadrat z wypełnieniem od góry po lewej do dołu po prawej"] # 0x25a7 + - "▨": [T: "kwadrat z wypełnieniem od góry po prawej do dołu po lewej"] # 0x25a8 + - "▩": [T: "kwadrat z ukośnym kreskowanym wypełnieniem"] # 0x25a9 + - "▪": [T: "czarny mały kwadrat"] # 0x25aa + - "▫": [T: "biały mały kwadrat"] # 0x25ab + - "▬": [T: "czarny prostokąt"] # 0x25ac + - "▭": [T: "biały prostokąt"] # 0x25ad + - "▮": [T: "czarny pionowy prostokąt"] # 0x25ae + - "▯": [T: "biały pionowy prostokąt"] # 0x25af + - "▰": [T: "czarny Równoległobok"] # 0x25b0 + - "▱": [T: "biały Równoległobok"] # 0x25b1 + - "▲": [T: "czarny trójkąt skierowany w górę"] # 0x25b2 + - "△": [T: "trójkąt"] # 0x25b3 + - "▴": [T: "czarny mały trójkąt skierowany w górę"] # 0x25b4 + - "▵": [T: "biały mały trójkąt skierowany w górę"] # 0x25b5 + - "▶": [T: "czarny trójkąt skierowany w prawo"] # 0x25b6 + - "▷": [T: "biały trójkąt skierowany w prawo"] # 0x25b7 + - "▸": [T: "czarny mały trójkąt skierowany w prawo"] # 0x25b8 + - "▹": [T: "biały mały trójkąt skierowany w prawo"] # 0x25b9 + - "►": [T: "czarny wskaźnik skierowany w prawo"] # 0x25ba + - "▻": [T: "biały wskaźnik skierowany w prawo"] # 0x25bb + - "▼": [T: "czarny trójkąt skierowany w dół"] # 0x25bc + - "▽": [T: "biały trójkąt skierowany w dół"] # 0x25bd + - "▾": [T: "czarny mały trójkąt skierowany w dół"] # 0x25be + - "▿": [T: "biały mały trójkąt skierowany w dół"] # 0x25bf + - "◀": [T: "czarny trójkąt skierowany w lewo"] # 0x25c0 + - "◁": [T: "biały trójkąt skierowany w lewo"] # 0x25c1 + - "◂": [T: "czarny mały trójkąt skierowany w lewo"] # 0x25c2 + - "◃": [T: "biały mały trójkąt skierowany w lewo"] # 0x25c3 + - "◄": [T: "czarny wskaźnik skierowany w lewo"] # 0x25c4 + - "◅": [T: "biały wskaźnik skierowany w lewo"] # 0x25c5 + - "◆": [T: "czarny romb"] # 0x25c6 + - "◇": [T: "biały romb"] # 0x25c7 + - "◈": [T: "biały romb zawierający czarny mały romb"] # 0x25c8 + - "◉": [T: "rybie oko"] # 0x25c9 + - "◊": [T: "romb"] # 0x25ca + - "○": [T: "białe koło"] # 0x25cb + - "◌": [T: "kropkowany okrąg"] # 0x25cc + - "◍": [T: "okrąg z pionowe wypełnienie"] # 0x25cd + - "◎": [T: "tarcza"] # 0x25ce + - "●": [T: "czarne koło"] # 0x25cf + - "◐": [T: "okrąg z czarną lewą połową"] # 0x25d0 + - "◑": [T: "okrąg z czarną prawą połową"] # 0x25d1 + - "◒": [T: "okrąg z czarną dolną połową"] # 0x25d2 + - "◓": [T: "okrąg z czarną górną połową"] # 0x25d3 + - "◔": [T: "okrąg z czarną górną prawą ćwiartką"] # 0x25d4 + - "◕": [T: "okrąg czarny poza górną lewą ćwiartką"] # 0x25d5 + - "◖": [T: "czarne koło z lewą połową"] # 0x25d6 + - "◗": [T: "czarne koło z prawą połową"] # 0x25d7 + - "◘": [T: "odwrotność punkt"] # 0x25d8 + - "◙": [T: "odwrotność białe koło"] # 0x25d9 + - "◚": [T: "górna połowa odwrotność białe koło"] # 0x25da + - "◛": [T: "dolna połowa odwrotność białe koło"] # 0x25db + - "◜": [T: "górny lewy ćwiartka łuk kołowy"] # 0x25dc + - "◝": [T: "górny prawy ćwiartka łuk kołowy"] # 0x25dd + - "◞": [T: "dolny prawy ćwiartka łuk kołowy"] # 0x25de + - "◟": [T: "dolny lewy ćwiartka łuk kołowy"] # 0x25df + - "◠": [T: "górna połowa okrąg"] # 0x25e0 + - "◡": [T: "dolna połowa okrąg"] # 0x25e1 + - "◢": [T: "czarny dolny prawy trójkąt"] # 0x25e2 + - "◣": [T: "czarny dolny lewy trójkąt"] # 0x25e3 + - "◤": [T: "czarny górny lewy trójkąt"] # 0x25e4 + - "◥": [T: "czarny górny prawy trójkąt"] # 0x25e5 + - "◦": [T: "złożenie"] # 0x25e6 + - "◧": [T: "kwadrat z czarną lewą połową"] # 0x25e7 + - "◨": [T: "kwadrat z czarną prawą połową"] # 0x25e8 + - "◩": [T: "kwadrat z czarną górną lewą połową"] # 0x25e9 + - "◪": [T: "kwadrat z czarną dolną prawą połową"] # 0x25ea + - "◫": [T: "biały kwadrat z linią dzielącą na pół"] # 0x25eb + - "◬": [T: "biały trójkąt skierowany w górę z kropką"] # 0x25ec + - "◭": [T: "trójkąt skierowany w górę z czarną lewą połową"] # 0x25ed + - "◮": [T: "trójkąt skierowany w górę z czarną prawą połową"] # 0x25ee + - "◯": [T: "duży okrąg"] # 0x25ef + - "◰": [T: "biały kwadrat z górną lewą ćwiartką"] # 0x25f0 + - "◱": [T: "biały kwadrat z dolną lewą ćwiartką"] # 0x25f1 + - "◲": [T: "biały kwadrat z dolną prawą ćwiartką"] # 0x25f2 + - "◳": [T: "biały kwadrat z górną prawą ćwiartką"] # 0x25f3 + - "◴": [T: "białe koło z górną lewą ćwiartką"] # 0x25f4 + - "◵": [T: "białe koło z dolną lewą ćwiartką"] # 0x25f5 + - "◶": [T: "białe koło z dolną prawą ćwiartką"] # 0x25f6 + - "◷": [T: "białe koło z górną prawą ćwiartką"] # 0x25f7 + - "◸": [T: "górny lewy trójkąt"] # 0x25f8 + - "◹": [T: "górny prawy trójkąt"] # 0x25f9 + - "◺": [T: "dolny lewy trójkąt"] # 0x25fa + - "◻": [T: "biały średni kwadrat"] # 0x25fb + - "◼": [T: "czarny średni kwadrat"] # 0x25fc + - "◽": [T: "biały średni mały kwadrat"] # 0x25fd + - "◾": [T: "czarny średni mały kwadrat"] # 0x25fe + - "◿": [T: "dolny prawy trójkąt"] # 0x25ff + - "★": [T: "czarna gwiazda"] # 0x2605 + - "☆": [T: "biały gwiazda"] # 0x2606 + - "☉": [T: "słońce"] # 0x2609 + - "☌": [T: "koniunkcja"] # 0x260c + - "☒": [T: "ramka wyboru z iksem"] # 0x2612 + - "☽": [T: "przybywający księżyc"] # 0x263d + - "☾": [T: "ubywający księżyc"] # 0x263e + - "☿": [T: "Merkury"] # 0x263f + - "♀": [T: "żeńskie"] # 0x2640 + - "♁": [T: "Ziemia"] # 0x2641 + - "♂": [T: "męskie"] # 0x2642 + - "♃": [T: "Jowisz"] # 0x2643 - "♄": [T: "Saturn"] # 0x2644 - - "♅": [t: "Uran"] # 0x2645 - - "♆": [t: "Neptun"] # 0x2646 - - "♇": [t: "Pluton"] # 0x2647 - - "♈": [t: "Baran"] # 0x2648 - - "♉": [t: "Byk"] # 0x2649 - - "♩": [t: "ćwierćnuta"] # 0x2669 - - "♭": [t: "bemol"] # 0x266d - - "♮": [t: "kasownik"] # 0x266e - - "♯": [t: "krzyżyk"] # 0x266f - - "♠": [t: "czarny pik"] # 0x2660 - - "♡": [t: "biały kier"] # 0x2661 - - "♢": [t: "biały karo"] # 0x2662 - - "♣": [t: "czarny trefl"] # 0x2663 - - "♤": [t: "biały pik"] # 0x2664 - - "♥": [t: "czarny kier"] # 0x2665 - - "♦": [t: "czarny karo"] # 0x2666 - - "♧": [t: "biały trefl"] # 0x2667 - - "⚀": [t: "ścianka kostki 1"] # 0x2680 - - "⚁": [t: "ścianka kostki 2"] # 0x2681 - - "⚂": [t: "ścianka kostki 3"] # 0x2682 - - "⚃": [t: "ścianka kostki 4"] # 0x2683 - - "⚄": [t: "ścianka kostki 5"] # 0x2684 - - "⚅": [t: "ścianka kostki 6"] # 0x2685 - - "⚆": [t: "białe koło z kropką prawy"] # 0x2686 - - "⚇": [t: "białe koło z dwiema kropkami"] # 0x2687 - - "⚈": [t: "czarne koło z kropką prawy"] # 0x2688 - - "⚉": [t: "czarne koło z dwiema kropkami"] # 0x2689 - - "⚪": [t: "średni białe koło"] # 0x26aa - - "⚫": [t: "średni czarne koło"] # 0x26ab - - "⚬": [t: "średni mały białe koło"] # 0x26ac - - "⚲": [t: "neutralny"] # 0x26b2 - - "✓": [t: "haczek znak"] # 0x2713 - - "✠": [t: "krzyż maltański"] # 0x2720 - - "✪": [t: "w kółku biały gwiazda"] # 0x272a - - "✶": [t: "czarna sześcioramienna gwiazda"] # 0x2736 - - "❨": [t: "średni lewy ornament nawiasu okrągłego"] # 0x2768 - - "❩": [t: "średni prawy ornament nawiasu okrągłego"] # 0x2769 - - "❪": [t: "średni spłaszczony lewy ornament nawiasu okrągłego"] # 0x276a - - "❫": [t: "średni spłaszczony prawy ornament nawiasu okrągłego"] # 0x276b - - "❬": [t: "średni kąt skierowany w lewo nawias kwadratowy ornament"] # 0x276c - - "❭": [t: "średni kąt skierowany w prawo nawias kwadratowy ornament"] # 0x276d - - "❮": [t: "gruby kąt skierowany w lewo cudzysłów ornament"] # 0x276e - - "❯": [t: "gruby kąt skierowany w prawo cudzysłów ornament"] # 0x276f - - "❰": [t: "gruby kąt skierowany w lewo nawias kwadratowy ornament"] # 0x2770 - - "❱": [t: "gruby kąt skierowany w prawo nawias kwadratowy ornament"] # 0x2771 - - "❲": [t: "lekki lewy nawias skorupowy ornament"] # 0x2772 - - "❳": [t: "lekki prawy nawias skorupowy ornament"] # 0x2773 - - "❴": [t: "średni lewy nawias klamrowy ornament"] # 0x2774 - - "❵": [t: "średni prawy nawias klamrowy ornament"] # 0x2775 - - "❶": [t: "czarny w kółku jeden"] # 0x2776 - - "❷": [t: "czarny w kółku dwa"] # 0x2777 - - "❸": [t: "czarny w kółku trzy"] # 0x2778 - - "❹": [t: "czarny w kółku cztery"] # 0x2779 - - "❺": [t: "czarny w kółku pięć"] # 0x277a - - "❻": [t: "czarny w kółku sześć"] # 0x277b - - "❼": [t: "czarny w kółku siedem"] # 0x277c - - "❽": [t: "czarny w kółku osiem"] # 0x277d - - "❾": [t: "czarny w kółku dziewięć"] # 0x277e - - "❿": [t: "czarny w kółku dziesięć"] # 0x277f - - "➀": [t: "w kółku bezszeryfowe jeden"] # 0x2780 - - "➁": [t: "w kółku bezszeryfowe dwa"] # 0x2781 - - "➂": [t: "w kółku bezszeryfowe trzy"] # 0x2782 - - "➃": [t: "w kółku bezszeryfowe cztery"] # 0x2783 - - "➄": [t: "w kółku bezszeryfowe pięć"] # 0x2784 - - "➅": [t: "w kółku bezszeryfowe sześć"] # 0x2785 - - "➆": [t: "w kółku bezszeryfowe siedem"] # 0x2786 - - "➇": [t: "w kółku bezszeryfowe osiem"] # 0x2787 - - "➈": [t: "w kółku bezszeryfowe dziewięć"] # 0x2788 - - "➉": [t: "w kółku bezszeryfowe dziesięć"] # 0x2789 - - "➊": [t: "czarny w kółku bezszeryfowe jeden"] # 0x278a - - "➋": [t: "czarny w kółku bezszeryfowe dwa"] # 0x278b - - "➌": [t: "czarny w kółku bezszeryfowe trzy"] # 0x278c - - "➍": [t: "czarny w kółku bezszeryfowe cztery"] # 0x278d - - "➎": [t: "czarny w kółku bezszeryfowe pięć"] # 0x278e - - "➏": [t: "czarny w kółku bezszeryfowe sześć"] # 0x278f - - "➐": [t: "czarny w kółku bezszeryfowe siedem"] # 0x2790 - - "➑": [t: "czarny w kółku bezszeryfowe osiem"] # 0x2791 - - "➒": [t: "czarny w kółku bezszeryfowe dziewięć"] # 0x2792 - - "➓": [t: "czarny w kółku bezszeryfowe dziesięć"] # 0x2793 - - "➔": [t: "gruba strzałka w prawo z szerokim grotem"] # 0x2794 - - "➕": [t: "gruby znak plus"] # 0x2795 - - "➖": [t: "gruby znak minus"] # 0x2796 - - "➗": [t: "gruby znak dzielenia"] # 0x2797 - - "➘": [t: "gruba strzałka na południowy wschód"] # 0x2798 - - "➙": [t: "gruby strzałka w prawo"] # 0x2799 - - "➚": [t: "gruba strzałka na północny wschód"] # 0x279a - - "➛": [t: "strzałka w prawo z grotem kreślarskim"] # 0x279b - - "➜": [t: "gruba strzałka w prawo z okrągłym grotem"] # 0x279c - - "➝": [t: "strzałka w prawo z trójkątnym grotem"] # 0x279d - - "➞": [t: "gruba strzałka w prawo z trójkątnym grotem"] # 0x279e - - "➟": [t: "kreskowana strzałka w prawo z trójkątnym grotem"] # 0x279f - - "➠": [t: "gruba kreskowana strzałka w prawo z trójkątnym grotem"] # 0x27a0 - - "➡": [t: "czarny strzałka w prawo"] # 0x27a1 - - "➢": [t: "trójwymiarowa strzałka w prawo podświetlona od góry"] # 0x27a2 - - "➣": [t: "trójwymiarowa strzałka w prawo podświetlona od dołu"] # 0x27a3 - - "➤": [t: "czarny prawy grot strzałki"] # 0x27a4 - - "➥": [t: "gruba czarna zakrzywiona dolna strzałka w prawo"] # 0x27a5 - - "➦": [t: "gruba czarna zakrzywiona górna strzałka w prawo"] # 0x27a6 - - "➧": [t: "krótki czarny strzałka w prawo"] # 0x27a7 - - "➨": [t: "gruba czarna wklęsła ostro zakończona strzałka w prawo"] # 0x27a8 - - "➩": [t: "prawostronnie cieniowana biała strzałka w prawo"] # 0x27a9 - - "➪": [t: "lewostronnie cieniowana biała strzałka w prawo"] # 0x27aa - - "➫": [t: "biała strzałka w prawo pochylona do tyłu z cieniem"] # 0x27ab - - "➬": [t: "biała strzałka w prawo pochylona do przodu z cieniem"] # 0x27ac - - "➭": [t: "gruba biała strzałka w prawo z cieniem w dolnym prawym rogu"] # 0x27ad - - "➮": [t: "gruba biała strzałka w prawo z cieniem w górnym prawym rogu"] # 0x27ae - - "➯": [t: "biała strzałka w prawo z dolnym prawym wcięciem i cieniem"] # 0x27af - - "➱": [t: "biała strzałka w prawo z górnym prawym wcięciem i cieniem"] # 0x27b1 - - "➲": [t: "gruba biała strzałka w prawo w kółku"] # 0x27b2 - - "➳": [t: "biała pierzasta strzałka w prawo"] # 0x27b3 - - "➴": [t: "czarna pierzasta strzałka południowo-wschodnia"] # 0x27b4 - - "➵": [t: "czarna pierzasta strzałka w prawo"] # 0x27b5 - - "➶": [t: "czarny pierzasty północ wschód strzałka"] # 0x27b6 - - "➷": [t: "gruby czarna pierzasta strzałka południowo-wschodnia"] # 0x27b7 - - "➸": [t: "gruby czarna pierzasta strzałka w prawo"] # 0x27b8 - - "➹": [t: "gruby czarny pierzasty północ wschód strzałka"] # 0x27b9 - - "➺": [t: "strzałka w prawo z łezkowatym grotem z zadziorami"] # 0x27ba - - "➻": [t: "gruba strzałka w prawo z łezkowatym grotem i trzonkiem"] # 0x27bb - - "➼": [t: "strzałka w prawo z klinowym ogonem"] # 0x27bc - - "➽": [t: "gruby strzałka w prawo z klinowym ogonem"] # 0x27bd - - "➾": [t: "otwarta konturowa strzałka w prawo"] # 0x27be - - "⟀": [t: "trójwymiarowy kąt"] # 0x27c0 - - "⟁": [t: "biały trójkąt zawierający mały biały trójkąt"] # 0x27c1 + - "♅": [T: "Uran"] # 0x2645 + - "♆": [T: "Neptun"] # 0x2646 + - "♇": [T: "Pluton"] # 0x2647 + - "♈": [T: "Baran"] # 0x2648 + - "♉": [T: "Byk"] # 0x2649 + - "♩": [T: "ćwierćnuta"] # 0x2669 + - "♭": [T: "bemol"] # 0x266d + - "♮": [T: "kasownik"] # 0x266e + - "♯": [T: "krzyżyk"] # 0x266f + - "♠": [T: "czarny pik"] # 0x2660 + - "♡": [T: "biały kier"] # 0x2661 + - "♢": [T: "biały karo"] # 0x2662 + - "♣": [T: "czarny trefl"] # 0x2663 + - "♤": [T: "biały pik"] # 0x2664 + - "♥": [T: "czarny kier"] # 0x2665 + - "♦": [T: "czarny karo"] # 0x2666 + - "♧": [T: "biały trefl"] # 0x2667 + - "⚀": [T: "ścianka kostki 1"] # 0x2680 + - "⚁": [T: "ścianka kostki 2"] # 0x2681 + - "⚂": [T: "ścianka kostki 3"] # 0x2682 + - "⚃": [T: "ścianka kostki 4"] # 0x2683 + - "⚄": [T: "ścianka kostki 5"] # 0x2684 + - "⚅": [T: "ścianka kostki 6"] # 0x2685 + - "⚆": [T: "białe koło z kropką prawy"] # 0x2686 + - "⚇": [T: "białe koło z dwiema kropkami"] # 0x2687 + - "⚈": [T: "czarne koło z kropką prawy"] # 0x2688 + - "⚉": [T: "czarne koło z dwiema kropkami"] # 0x2689 + - "⚪": [T: "średni białe koło"] # 0x26aa + - "⚫": [T: "średni czarne koło"] # 0x26ab + - "⚬": [T: "średni mały białe koło"] # 0x26ac + - "⚲": [T: "neutralny"] # 0x26b2 + - "✓": [T: "haczek znak"] # 0x2713 + - "✠": [T: "krzyż maltański"] # 0x2720 + - "✪": [T: "w kółku biały gwiazda"] # 0x272a + - "✶": [T: "czarna sześcioramienna gwiazda"] # 0x2736 + - "❨": [T: "średni lewy ornament nawiasu okrągłego"] # 0x2768 + - "❩": [T: "średni prawy ornament nawiasu okrągłego"] # 0x2769 + - "❪": [T: "średni spłaszczony lewy ornament nawiasu okrągłego"] # 0x276a + - "❫": [T: "średni spłaszczony prawy ornament nawiasu okrągłego"] # 0x276b + - "❬": [T: "średni kąt skierowany w lewo nawias kwadratowy ornament"] # 0x276c + - "❭": [T: "średni kąt skierowany w prawo nawias kwadratowy ornament"] # 0x276d + - "❮": [T: "gruby kąt skierowany w lewo cudzysłów ornament"] # 0x276e + - "❯": [T: "gruby kąt skierowany w prawo cudzysłów ornament"] # 0x276f + - "❰": [T: "gruby kąt skierowany w lewo nawias kwadratowy ornament"] # 0x2770 + - "❱": [T: "gruby kąt skierowany w prawo nawias kwadratowy ornament"] # 0x2771 + - "❲": [T: "lekki lewy nawias skorupowy ornament"] # 0x2772 + - "❳": [T: "lekki prawy nawias skorupowy ornament"] # 0x2773 + - "❴": [T: "średni lewy nawias klamrowy ornament"] # 0x2774 + - "❵": [T: "średni prawy nawias klamrowy ornament"] # 0x2775 + - "❶": [T: "czarny w kółku jeden"] # 0x2776 + - "❷": [T: "czarny w kółku dwa"] # 0x2777 + - "❸": [T: "czarny w kółku trzy"] # 0x2778 + - "❹": [T: "czarny w kółku cztery"] # 0x2779 + - "❺": [T: "czarny w kółku pięć"] # 0x277a + - "❻": [T: "czarny w kółku sześć"] # 0x277b + - "❼": [T: "czarny w kółku siedem"] # 0x277c + - "❽": [T: "czarny w kółku osiem"] # 0x277d + - "❾": [T: "czarny w kółku dziewięć"] # 0x277e + - "❿": [T: "czarny w kółku dziesięć"] # 0x277f + - "➀": [T: "w kółku bezszeryfowe jeden"] # 0x2780 + - "➁": [T: "w kółku bezszeryfowe dwa"] # 0x2781 + - "➂": [T: "w kółku bezszeryfowe trzy"] # 0x2782 + - "➃": [T: "w kółku bezszeryfowe cztery"] # 0x2783 + - "➄": [T: "w kółku bezszeryfowe pięć"] # 0x2784 + - "➅": [T: "w kółku bezszeryfowe sześć"] # 0x2785 + - "➆": [T: "w kółku bezszeryfowe siedem"] # 0x2786 + - "➇": [T: "w kółku bezszeryfowe osiem"] # 0x2787 + - "➈": [T: "w kółku bezszeryfowe dziewięć"] # 0x2788 + - "➉": [T: "w kółku bezszeryfowe dziesięć"] # 0x2789 + - "➊": [T: "czarny w kółku bezszeryfowe jeden"] # 0x278a + - "➋": [T: "czarny w kółku bezszeryfowe dwa"] # 0x278b + - "➌": [T: "czarny w kółku bezszeryfowe trzy"] # 0x278c + - "➍": [T: "czarny w kółku bezszeryfowe cztery"] # 0x278d + - "➎": [T: "czarny w kółku bezszeryfowe pięć"] # 0x278e + - "➏": [T: "czarny w kółku bezszeryfowe sześć"] # 0x278f + - "➐": [T: "czarny w kółku bezszeryfowe siedem"] # 0x2790 + - "➑": [T: "czarny w kółku bezszeryfowe osiem"] # 0x2791 + - "➒": [T: "czarny w kółku bezszeryfowe dziewięć"] # 0x2792 + - "➓": [T: "czarny w kółku bezszeryfowe dziesięć"] # 0x2793 + - "➔": [T: "gruba strzałka w prawo z szerokim grotem"] # 0x2794 + - "➕": [T: "gruby znak plus"] # 0x2795 + - "➖": [T: "gruby znak minus"] # 0x2796 + - "➗": [T: "gruby znak dzielenia"] # 0x2797 + - "➘": [T: "gruba strzałka na południowy wschód"] # 0x2798 + - "➙": [T: "gruby strzałka w prawo"] # 0x2799 + - "➚": [T: "gruba strzałka na północny wschód"] # 0x279a + - "➛": [T: "strzałka w prawo z grotem kreślarskim"] # 0x279b + - "➜": [T: "gruba strzałka w prawo z okrągłym grotem"] # 0x279c + - "➝": [T: "strzałka w prawo z trójkątnym grotem"] # 0x279d + - "➞": [T: "gruba strzałka w prawo z trójkątnym grotem"] # 0x279e + - "➟": [T: "kreskowana strzałka w prawo z trójkątnym grotem"] # 0x279f + - "➠": [T: "gruba kreskowana strzałka w prawo z trójkątnym grotem"] # 0x27a0 + - "➡": [T: "czarny strzałka w prawo"] # 0x27a1 + - "➢": [T: "trójwymiarowa strzałka w prawo podświetlona od góry"] # 0x27a2 + - "➣": [T: "trójwymiarowa strzałka w prawo podświetlona od dołu"] # 0x27a3 + - "➤": [T: "czarny prawy grot strzałki"] # 0x27a4 + - "➥": [T: "gruba czarna zakrzywiona dolna strzałka w prawo"] # 0x27a5 + - "➦": [T: "gruba czarna zakrzywiona górna strzałka w prawo"] # 0x27a6 + - "➧": [T: "krótki czarny strzałka w prawo"] # 0x27a7 + - "➨": [T: "gruba czarna wklęsła ostro zakończona strzałka w prawo"] # 0x27a8 + - "➩": [T: "prawostronnie cieniowana biała strzałka w prawo"] # 0x27a9 + - "➪": [T: "lewostronnie cieniowana biała strzałka w prawo"] # 0x27aa + - "➫": [T: "biała strzałka w prawo pochylona do tyłu z cieniem"] # 0x27ab + - "➬": [T: "biała strzałka w prawo pochylona do przodu z cieniem"] # 0x27ac + - "➭": [T: "gruba biała strzałka w prawo z cieniem w dolnym prawym rogu"] # 0x27ad + - "➮": [T: "gruba biała strzałka w prawo z cieniem w górnym prawym rogu"] # 0x27ae + - "➯": [T: "biała strzałka w prawo z dolnym prawym wcięciem i cieniem"] # 0x27af + - "➱": [T: "biała strzałka w prawo z górnym prawym wcięciem i cieniem"] # 0x27b1 + - "➲": [T: "gruba biała strzałka w prawo w kółku"] # 0x27b2 + - "➳": [T: "biała pierzasta strzałka w prawo"] # 0x27b3 + - "➴": [T: "czarna pierzasta strzałka południowo-wschodnia"] # 0x27b4 + - "➵": [T: "czarna pierzasta strzałka w prawo"] # 0x27b5 + - "➶": [T: "czarny pierzasty północ wschód strzałka"] # 0x27b6 + - "➷": [T: "gruby czarna pierzasta strzałka południowo-wschodnia"] # 0x27b7 + - "➸": [T: "gruby czarna pierzasta strzałka w prawo"] # 0x27b8 + - "➹": [T: "gruby czarny pierzasty północ wschód strzałka"] # 0x27b9 + - "➺": [T: "strzałka w prawo z łezkowatym grotem z zadziorami"] # 0x27ba + - "➻": [T: "gruba strzałka w prawo z łezkowatym grotem i trzonkiem"] # 0x27bb + - "➼": [T: "strzałka w prawo z klinowym ogonem"] # 0x27bc + - "➽": [T: "gruby strzałka w prawo z klinowym ogonem"] # 0x27bd + - "➾": [T: "otwarta konturowa strzałka w prawo"] # 0x27be + - "⟀": [T: "trójwymiarowy kąt"] # 0x27c0 + - "⟁": [T: "biały trójkąt zawierający mały biały trójkąt"] # 0x27c1 - "⟂⊥": # 0x27c2, 22A5 (22A5 wrongly used for perpendicular by most editors) - test: - if: "$SpeechStyle = 'LiteralSpeak' and text()='⊥'" # 22A5 (up tack/bottom) @@ -1670,1327 +1670,1327 @@ - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "otwórz podzbiór" + - T: "otwórz podzbiór" - "⟄": # 0x27c4 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "otwórz nadzbiór" - - "⟅": [t: "lewy ogranicznik workowy w kształcie S"] # 0x27c5 - - "⟆": [t: "prawy ogranicznik workowy w kształcie S"] # 0x27c6 - - "⟇": [t: "lub z kropką wewnątrz"] # 0x27c7 - - "⟈": [t: "odwrotny ukośnik poprzedzający podzbiór"] # 0x27c8 - - "⟉": [t: "nadzbiór poprzedzający ukośnik"] # 0x27c9 - - "⟊": [t: "pionowa kreska z poziomą kreską"] # 0x27ca - - "⟋": [t: "matematyczny ukośnik wznoszący"] # 0x27cb - - "⟌": [t: "dzielenie pisemne"] # 0x27cc - - "⟍": [t: "matematyczny ukośnik opadający"] # 0x27cd - - "⟎": [t: "kwadratowa koniunkcja logiczna"] # 0x27ce - - "⟏": [t: "do kwadratu logiczne lub"] # 0x27cf - - "⟐": [t: "biały romb z wyśrodkowaną kropką"] # 0x27d0 - - "⟑": [t: "i z kropką"] # 0x27d1 - - "⟒": [t: "należy do z otwarciem u góry"] # 0x27d2 - - "⟓": [t: "prawy dolny narożnik z kropką"] # 0x27d3 - - "⟔": [t: "górny lewy narożnik z kropką"] # 0x27d4 - - "⟕": [t: "lewe złączenie zewnętrzne"] # 0x27d5 - - "⟖": [t: "prawe złączenie zewnętrzne"] # 0x27d6 - - "⟗": [t: "pełne złączenie zewnętrzne"] # 0x27d7 - - "⟘": [t: "duży górny znacznik"] # 0x27d8 - - "⟙": [t: "duży dolny znacznik"] # 0x27d9 - - "⟚": [t: "lewy i prawy podwójny znacznik dedukcji"] # 0x27da - - "⟛": [t: "lewy i prawy znacznik"] # 0x27db - - "⟜": [t: "lewa multimapa"] # 0x27dc - - "⟝": [t: "długi prawy znacznik"] # 0x27dd - - "⟞": [t: "długi lewy znacznik"] # 0x27de - - "⟟": [t: "górny znacznik z okręgiem nad"] # 0x27df - - "⟠": [t: "romb podzielony poziomą kreską"] # 0x27e0 - - "⟡": [t: "biały romb o wklęsłych bokach"] # 0x27e1 - - "⟢": [t: "biały romb o wklęsłych bokach z lewym haczykiem"] # 0x27e2 - - "⟣": [t: "biały romb o wklęsłych bokach z prawym haczykiem"] # 0x27e3 - - "⟤": [t: "biały kwadrat z lewym haczykiem"] # 0x27e4 - - "⟥": [t: "biały kwadrat z prawym haczykiem"] # 0x27e5 - - "⟦": [t: "lewy biały nawias kwadratowy"] # 0x27e6 - - "⟧": [t: "prawy biały nawias kwadratowy"] # 0x27e7 - - "⟨": [t: "lewy nawias kątowy"] # 0x27e8 - - "⟩": [t: "prawy nawias kątowy"] # 0x27e9 - - "⟪": [t: "lewy podwójny nawias kątowy"] # 0x27ea - - "⟫": [t: "prawy podwójny nawias kątowy"] # 0x27eb - - "⟬": [t: "lewy biały nawias skorupowy"] # 0x27ec - - "⟭": [t: "prawy biały nawias skorupowy"] # 0x27ed - - "⟮": [t: "lewy spłaszczony nawias"] # 0x27ee - - "⟯": [t: "prawy spłaszczony nawias"] # 0x27ef - - "⟰": [t: "poczwórna strzałka w górę"] # 0x27f0 - - "⟱": [t: "poczwórna strzałka w dół"] # 0x27f1 - - "⟲": [t: "okrężna strzałka z przerwą przeciwnie do ruchu wskazówek zegara"] # 0x27f2 - - "⟳": [t: "okrężna strzałka z przerwą zgodnie z ruchem wskazówek zegara"] # 0x27f3 - - "⟴": [t: "strzałka w prawo z plusem w kółku"] # 0x27f4 - - "⟵": [t: "długa strzałka w lewo"] # 0x27f5 - - "⟶": [t: "długa strzałka w prawo"] # 0x27f6 - - "⟷": [t: "długa strzałka w lewo i w prawo"] # 0x27f7 - - "⟸": [t: "długa podwójna strzałka w lewo"] # 0x27f8 - - "⟹": [t: "implikuje"] # 0x27f9 - - "⟺": [t: "wtedy i tylko wtedy"] # 0x27fa - - "⟻": [t: "długa strzałka w lewo od kreski"] # 0x27fb - - "⟼": [t: "długa strzałka w prawo od kreski"] # 0x27fc - - "⟽": [t: "długa podwójna strzałka w lewo od kreski"] # 0x27fd - - "⟾": [t: "długa podwójna strzałka w prawo od kreski"] # 0x27fe - - "⟿": [t: "długa falista strzałka w prawo"] # 0x27ff - - "⤀": [t: "strzałka w prawo z dwoma grotami i pionowym przekreśleniem"] # 0x2900 - - "⤁": [t: "strzałka w prawo z dwoma grotami i podwójnym pionowym przekreśleniem"] # 0x2901 - - "⤂": [t: "podwójna strzałka w lewo z pionową kreską"] # 0x2902 - - "⤃": [t: "podwójna strzałka w prawo z pionową kreską"] # 0x2903 - - "⤄": [t: "podwójna strzałka w lewo i w prawo z pionową kreską"] # 0x2904 - - "⤅": [t: "strzałka w prawo z dwoma grotami od kreski"] # 0x2905 - - "⤆": [t: "podwójna strzałka w lewo od kreski"] # 0x2906 - - "⤇": [t: "podwójna strzałka w prawo od kreski"] # 0x2907 - - "⤈": [t: "strzałka w dół z poziomą kreską"] # 0x2908 - - "⤉": [t: "strzałka w górę z poziomą kreską"] # 0x2909 - - "⤊": [t: "górny potrójny strzałka"] # 0x290a - - "⤋": [t: "dolny potrójny strzałka"] # 0x290b - - "⤌": [t: "lewy podwójny kreska strzałka"] # 0x290c - - "⤍": [t: "prawy podwójny kreska strzałka"] # 0x290d - - "⤎": [t: "lewy potrójny kreska strzałka"] # 0x290e - - "⤏": [t: "prawy potrójny kreska strzałka"] # 0x290f - - "⤐": [t: "strzałka w prawo z dwoma grotami i potrójną kreską"] # 0x2910 - - "⤑": [t: "strzałka w prawo z kropkowanym trzonem"] # 0x2911 - - "⤒": [t: "strzałka w górę do kreski"] # 0x2912 - - "⤓": [t: "strzałka w dół do kreski"] # 0x2913 - - "⤔": [t: "strzałka w prawo z ogonem i pionową kreską"] # 0x2914 - - "⤕": [t: "strzałka w prawo z ogonem i podwójną pionową kreską"] # 0x2915 - - "⤖": [t: "strzałka w prawo z dwoma grotami i ogonem"] # 0x2916 - - "⤗": [t: "strzałka w prawo z dwoma grotami, ogonem i pionowym przekreśleniem"] # 0x2917 - - "⤘": [t: "strzałka w prawo z dwoma grotami, ogonem i podwójnym pionowym przekreśleniem"] # 0x2918 - - "⤙": [t: "strzałka w lewo ogon"] # 0x2919 - - "⤚": [t: "strzałka w prawo ogon"] # 0x291a - - "⤛": [t: "lewy podwójna strzałka ogon"] # 0x291b - - "⤜": [t: "prawy podwójna strzałka ogon"] # 0x291c - - "⤝": [t: "strzałka w lewo do wypełnionego rombu"] # 0x291d - - "⤞": [t: "strzałka w prawo do wypełnionego rombu"] # 0x291e - - "⤟": [t: "strzałka w lewo od kreski do wypełnionego rombu"] # 0x291f - - "⤠": [t: "strzałka w prawo od kreski do wypełnionego rombu"] # 0x2920 - - "⤡": [t: "strzałka północno-zachodnia i południowo-wschodnia"] # 0x2921 - - "⤢": [t: "strzałka północno-wschodnia i południowo-zachodnia"] # 0x2922 - - "⤣": [t: "strzałka północno-zachodnia z hakiem"] # 0x2923 - - "⤤": [t: "strzałka północno-wschodnia z hakiem"] # 0x2924 - - "⤥": [t: "strzałka południowo-wschodnia z hakiem"] # 0x2925 - - "⤦": [t: "strzałka południowo-zachodnia z hakiem"] # 0x2926 - - "⤧": [t: "strzałka północno-zachodnia i strzałka północno-wschodnia"] # 0x2927 - - "⤨": [t: "strzałka północno-wschodnia i strzałka południowo-wschodnia"] # 0x2928 - - "⤩": [t: "strzałka południowo-wschodnia i strzałka południowo-zachodnia"] # 0x2929 - - "⤪": [t: "strzałka południowo-zachodnia i strzałka północno-zachodnia"] # 0x292a - - "⤫": [t: "ukośna wznosząca przecinająca ukośną opadającą"] # 0x292b - - "⤬": [t: "ukośna opadająca przecinająca ukośną wznoszącą"] # 0x292c - - "⤭": [t: "strzałka południowo-wschodnia przecinająca strzałkę północno-wschodnią"] # 0x292d - - "⤮": [t: "strzałka północno-wschodnia przecinająca strzałkę południowo-wschodnią"] # 0x292e - - "⤯": [t: "ukośna opadająca przecinająca strzałkę północno-wschodnią"] # 0x292f - - "⤰": [t: "ukośna wznosząca przecinająca strzałkę południowo-wschodnią"] # 0x2930 - - "⤱": [t: "strzałka północno-wschodnia przecinająca strzałkę północno-zachodnią"] # 0x2931 - - "⤲": [t: "strzałka północno-zachodnia przecinająca strzałkę północno-wschodnią"] # 0x2932 - - "⤳": [t: "falista strzałka skierowana bezpośrednio w prawo"] # 0x2933 - - "⤴": [t: "strzałka skierowana w prawo, potem zakrzywiona w górę"] # 0x2934 - - "⤵": [t: "strzałka skierowana w prawo, potem zakrzywiona w dół"] # 0x2935 - - "⤶": [t: "strzałka skierowana w dół, potem zakrzywiona w lewo"] # 0x2936 - - "⤷": [t: "strzałka skierowana w dół, potem zakrzywiona w prawo"] # 0x2937 - - "⤸": [t: "prawostronna strzałka łukowa zgodna z ruchem wskazówek zegara"] # 0x2938 - - "⤹": [t: "lewostronna strzałka łukowa przeciwna do ruchu wskazówek zegara"] # 0x2939 - - "⤺": [t: "górna strzałka łukowa przeciwna do ruchu wskazówek zegara"] # 0x293a - - "⤻": [t: "dolna strzałka łukowa przeciwna do ruchu wskazówek zegara"] # 0x293b - - "⤼": [t: "górna strzałka łukowa zgodna z ruchem wskazówek zegara z minusem"] # 0x293c - - "⤽": [t: "górna strzałka łukowa przeciwna do ruchu wskazówek zegara z plusem"] # 0x293d - - "⤾": [t: "dolna prawa strzałka półokrągła zgodna z ruchem wskazówek zegara"] # 0x293e - - "⤿": [t: "dolna lewa strzałka półokrągła przeciwna do ruchu wskazówek zegara"] # 0x293f - - "⥀": [t: "zamknięta strzałka okrężna przeciwna do ruchu wskazówek zegara"] # 0x2940 - - "⥁": [t: "zamknięta strzałka okrężna zgodna z ruchem wskazówek zegara"] # 0x2941 - - "⥂": [t: "strzałka w prawo nad krótką strzałką w lewo"] # 0x2942 - - "⥃": [t: "strzałka w lewo nad krótką strzałką w prawo"] # 0x2943 - - "⥄": [t: "krótka strzałka w prawo nad strzałką w lewo"] # 0x2944 - - "⥅": [t: "strzałka w prawo z plusem pod spodem"] # 0x2945 - - "⥆": [t: "strzałka w lewo z plusem pod spodem"] # 0x2946 - - "⥇": [t: "strzałka w prawo przez X"] # 0x2947 - - "⥈": [t: "strzałka w lewo i w prawo przez okrąg"] # 0x2948 - - "⥉": [t: "strzałka w górę z dwoma grotami od okręgu"] # 0x2949 - - "⥊": [t: "lewy grot górny prawy grot dolny harpun"] # 0x294a - - "⥋": [t: "lewy grot dolny prawy grot górny harpun"] # 0x294b - - "⥌": [t: "górny grot prawy dolny grot lewy harpun"] # 0x294c - - "⥍": [t: "górny grot lewy dolny grot prawy harpun"] # 0x294d - - "⥎": [t: "lewy grot górny prawy grot górny harpun"] # 0x294e - - "⥏": [t: "górny grot prawy dolny grot prawy harpun"] # 0x294f - - "⥐": [t: "lewy grot dolny prawy grot dolny harpun"] # 0x2950 - - "⥑": [t: "górny grot lewy dolny grot lewy harpun"] # 0x2951 - - "⥒": [t: "lewy harpun z grotem górnym do kreski"] # 0x2952 - - "⥓": [t: "prawy harpun z grotem górnym do kreski"] # 0x2953 - - "⥔": [t: "górny harpun z grotem prawym do kreski"] # 0x2954 - - "⥕": [t: "dolny harpun z grotem prawym do kreski"] # 0x2955 - - "⥖": [t: "lewy harpun z grotem dolnym do kreski"] # 0x2956 - - "⥗": [t: "prawy harpun z grotem dolnym do kreski"] # 0x2957 - - "⥘": [t: "górny harpun z grotem lewym do kreski"] # 0x2958 - - "⥙": [t: "dolny harpun z grotem lewym do kreski"] # 0x2959 - - "⥚": [t: "lewy harpun z grotem górnym od kreski"] # 0x295a - - "⥛": [t: "prawy harpun z grotem górnym od kreski"] # 0x295b - - "⥜": [t: "górny harpun z grotem prawym od kreski"] # 0x295c - - "⥝": [t: "dolny harpun z grotem prawym od kreski"] # 0x295d - - "⥞": [t: "lewy harpun z grotem dolnym od kreski"] # 0x295e - - "⥟": [t: "prawy harpun z grotem dolnym od kreski"] # 0x295f - - "⥠": [t: "górny harpun z grotem lewym od kreski"] # 0x2960 - - "⥡": [t: "dolny harpun z grotem lewym od kreski"] # 0x2961 - - "⥢": [t: "lewy harpun z grotem górnym nad lewy harpun z grotem dolnym"] # 0x2962 - - "⥣": [t: "górny harpun z grotem w lewo obok górnego harpuna z grotem w prawo"] # 0x2963 - - "⥤": [t: "prawy harpun z grotem górnym nad prawy harpun z grotem dolnym"] # 0x2964 - - "⥥": [t: "dolny harpun z grotem w lewo obok dolnego harpuna z grotem w prawo"] # 0x2965 - - "⥦": [t: "lewy harpun z grotem górnym nad prawy harpun z grotem górnym"] # 0x2966 - - "⥧": [t: "lewy harpun z grotem dolnym nad prawy harpun z grotem dolnym"] # 0x2967 - - "⥨": [t: "prawy harpun z grotem górnym nad lewy harpun z grotem górnym"] # 0x2968 - - "⥩": [t: "prawy harpun z grotem dolnym nad lewy harpun z grotem dolnym"] # 0x2969 - - "⥪": [t: "lewy harpun z grotem górnym nad długą kreską"] # 0x296a - - "⥫": [t: "lewy harpun z grotem dolnym pod długą kreską"] # 0x296b - - "⥬": [t: "prawy harpun z grotem górnym nad długą kreską"] # 0x296c - - "⥭": [t: "prawy harpun z grotem dolnym pod długą kreską"] # 0x296d - - "⥮": [t: "górny harpun z grotem w lewo obok dolnego harpuna z grotem w prawo"] # 0x296e - - "⥯": [t: "dolny harpun z grotem w lewo obok górnego harpuna z grotem w prawo"] # 0x296f - - "⥰": [t: "podwójna strzałka w prawo z zaokrąglonym grotem"] # 0x2970 - - "⥱": [t: "równa się nad strzałka w prawo"] # 0x2971 - - "⥲": [t: "operator tyldy nad strzałką w prawo"] # 0x2972 - - "⥳": [t: "strzałka w lewo nad operatorem tyldy"] # 0x2973 - - "⥴": [t: "strzałka w prawo nad operatorem tyldy"] # 0x2974 - - "⥵": [t: "strzałka w prawo nad znakiem przybliżonej równości"] # 0x2975 - - "⥶": [t: "mniejsze niż nad strzałka w lewo"] # 0x2976 - - "⥷": [t: "strzałka w lewo przez znak mniejsze niż"] # 0x2977 - - "⥸": [t: "większe niż nad strzałka w prawo"] # 0x2978 - - "⥹": [t: "podzbiór nad strzałka w prawo"] # 0x2979 - - "⥺": [t: "strzałka w lewo przez znak podzbioru"] # 0x297a - - "⥻": [t: "nadzbiór nad strzałka w lewo"] # 0x297b - - "⥼": [t: "lewy rybi ogon"] # 0x297c - - "⥽": [t: "prawy rybi ogon"] # 0x297d - - "⥾": [t: "górny rybi ogon"] # 0x297e - - "⥿": [t: "dolny rybi ogon"] # 0x297f - - "⦀": [t: "potrójna pionowa kreska jako ogranicznik"] # 0x2980 - - "⦁": [t: "punkt w notacji Z"] # 0x2981 - - "⦂": [t: "dwukropek typu w notacji Z"] # 0x2982 - - "⦃": [t: "lewy biały nawias klamrowy"] # 0x2983 - - "⦄": [t: "prawy biały nawias klamrowy"] # 0x2984 - - "⦅": [t: "lewy biały nawias"] # 0x2985 - - "⦆": [t: "prawy biały nawias"] # 0x2986 - - "⦇": [t: "lewy nawias kwadratowy obrazu w notacji Z"] # 0x2987 - - "⦈": [t: "prawy nawias kwadratowy obrazu w notacji Z"] # 0x2988 - - "⦉": [t: "lewy nawias kwadratowy wiązania w notacji Z"] # 0x2989 - - "⦊": [t: "prawy nawias kwadratowy wiązania w notacji Z"] # 0x298a - - "⦋": [t: "lewy nawias kwadratowy z podkreśleniem"] # 0x298b - - "⦌": [t: "prawy nawias kwadratowy z podkreśleniem"] # 0x298c - - "⦍": [t: "lewy nawias kwadratowy z haczykiem w górnym rogu"] # 0x298d - - "⦎": [t: "prawy nawias kwadratowy z haczykiem w dolnym rogu"] # 0x298e - - "⦏": [t: "lewy nawias kwadratowy z haczykiem w dolnym rogu"] # 0x298f - - "⦐": [t: "prawy nawias kwadratowy z haczykiem w górnym rogu"] # 0x2990 - - "⦑": [t: "lewy kąt nawias kwadratowy z kropką"] # 0x2991 - - "⦒": [t: "prawy nawias kątowy z kropką"] # 0x2992 - - "⦓": [t: "lewy łuk mniejsze niż nawias kwadratowy"] # 0x2993 - - "⦔": [t: "prawy łuk większe niż nawias kwadratowy"] # 0x2994 - - "⦕": [t: "podwójny lewy łuk większe niż nawias kwadratowy"] # 0x2995 - - "⦖": [t: "podwójny prawy łuk mniejsze niż nawias kwadratowy"] # 0x2996 - - "⦗": [t: "lewy czarny nawias skorupowy"] # 0x2997 - - "⦘": [t: "prawy czarny nawias skorupowy"] # 0x2998 - - "⦙": [t: "kropkowany ogranicznik"] # 0x2999 - - "⦚": [t: "pionowa linia zygzakowata"] # 0x299a - - "⦛": [t: "kąt mierzony otwarty w lewo"] # 0x299b - - "⦜": [t: "wariant kąta prostego z kwadratem"] # 0x299c - - "⦝": [t: "mierzony kąt prosty z kropką"] # 0x299d - - "⦞": [t: "kąt z s wewnątrz"] # 0x299e - - "⦟": [t: "akut kąt"] # 0x299f - - "⦠": [t: "kąt sferyczny otwarty w lewo"] # 0x29a0 - - "⦡": [t: "kąt sferyczny otwarty w górę"] # 0x29a1 - - "⦢": [t: "odwrócony kąt"] # 0x29a2 - - "⦣": [t: "kąt odwrócony"] # 0x29a3 - - "⦤": [t: "kąt z podkreśleniem"] # 0x29a4 - - "⦥": [t: "kąt odwrócony z podkreśleniem"] # 0x29a5 - - "⦦": [t: "kąt ukośny otwarty w górę"] # 0x29a6 - - "⦧": [t: "kąt ukośny otwarty w dół"] # 0x29a7 - - "⦨": [t: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w górę i w prawo"] # 0x29a8 - - "⦩": [t: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w górę i w lewo"] # 0x29a9 - - "⦪": [t: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w dół i w prawo"] # 0x29aa - - "⦫": [t: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w dół i w lewo"] # 0x29ab - - "⦬": [t: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w prawo i w górę"] # 0x29ac - - "⦭": [t: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w lewo i w górę"] # 0x29ad - - "⦮": [t: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w prawo i w dół"] # 0x29ae - - "⦯": [t: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w lewo i w dół"] # 0x29af - - "⦰": [t: "odwrócony zbiór pusty"] # 0x29b0 - - "⦱": [t: "zbiór pusty z kreską nad"] # 0x29b1 - - "⦲": [t: "zbiór pusty z małym okręgiem nad"] # 0x29b2 - - "⦳": [t: "zbiór pusty ze strzałką w prawo nad"] # 0x29b3 - - "⦴": [t: "zbiór pusty ze strzałką w lewo nad"] # 0x29b4 - - "⦵": [t: "okrąg z kreską poziomą"] # 0x29b5 - - "⦶": [t: "pionowa kreska w kółku"] # 0x29b6 - - "⦷": [t: "w kółku równoległe"] # 0x29b7 - - "⦸": [t: "ukośnik odwrotny w kółku"] # 0x29b8 - - "⦹": [t: "w kółku prostopadłe"] # 0x29b9 - - "⦺": [t: "okrąg podzielony kreską poziomą, z górną połową podzieloną kreską pionową"] # 0x29ba - - "⦻": [t: "okrąg z nałożonym X"] # 0x29bb - - "⦼": [t: "znak dzielenia w kółku obrócony przeciwnie do ruchu wskazówek zegara"] # 0x29bc - - "⦽": [t: "strzałka w górę przez okrąg"] # 0x29bd - - "⦾": [t: "w kółku biały punkt"] # 0x29be - - "⦿": [t: "w kółku punkt"] # 0x29bf - - "⧀": [t: "w kółku mniejsze niż"] # 0x29c0 - - "⧁": [t: "w kółku większe niż"] # 0x29c1 - - "⧂": [t: "okrąg z małym okręgiem po prawej"] # 0x29c2 - - "⧃": [t: "okrąg z dwiema poziomymi kreskami po prawej"] # 0x29c3 - - "⧄": [t: "kwadrat z ukośnikiem wznoszącym"] # 0x29c4 - - "⧅": [t: "kwadrat z ukośnikiem opadającym"] # 0x29c5 - - "⧆": [t: "do kwadratu gwiazdka"] # 0x29c6 - - "⧇": [t: "do kwadratu mały okrąg"] # 0x29c7 - - "⧈": [t: "do kwadratu kwadrat"] # 0x29c8 - - "⧉": [t: "dwa połączone kwadraty"] # 0x29c9 - - "⧊": [t: "trójkąt z kropką nad"] # 0x29ca - - "⧋": [t: "trójkąt z podkreśleniem"] # 0x29cb - - "⧌": [t: "s w trójkąt"] # 0x29cc - - "⧍": [t: "trójkąt z szeryfami u dołu"] # 0x29cd - - "⧎": [t: "prawy trójkąt nad lewy trójkąt"] # 0x29ce - - "⧏": [t: "lewy trójkąt obok pionowej kreski"] # 0x29cf - - "⧐": [t: "pionowa kreska obok prawego trójkąta"] # 0x29d0 - - "⧑": [t: "kokarda z lewą połową czarną"] # 0x29d1 - - "⧒": [t: "kokarda z prawą połową czarną"] # 0x29d2 - - "⧓": [t: "czarna kokarda"] # 0x29d3 - - "⧔": [t: "znak mnożenia z lewą połową czarną"] # 0x29d4 - - "⧕": [t: "znak mnożenia z prawą połową czarną"] # 0x29d5 - - "⧖": [t: "biały klepsydra"] # 0x29d6 - - "⧗": [t: "czarny klepsydra"] # 0x29d7 - - "⧘": [t: "lewy falisty ogranicznik"] # 0x29d8 - - "⧙": [t: "prawy falisty ogranicznik"] # 0x29d9 - - "⧚": [t: "lewy podwójny falisty ogranicznik"] # 0x29da - - "⧛": [t: "prawy podwójny falisty ogranicznik"] # 0x29db - - "⧜": [t: "niepełna nieskończoność"] # 0x29dc - - "⧝": [t: "wiązanie nad nieskończonością"] # 0x29dd - - "⧞": [t: "zanegowana nieskończoność z kreską pionową"] # 0x29de - - "⧟": [t: "podwójnie zakończona strzałka mapująca"] # 0x29df - - "⧠": [t: "kwadrat z konturowym obrysem"] # 0x29e0 - - "⧡": [t: "wzrasta jak"] # 0x29e1 - - "⧢": [t: "iloczyn przetasowania"] # 0x29e2 - - "⧣": [t: "znak równości i ukośne równoległe"] # 0x29e3 - - "⧤": [t: "znak równości i ukośne równoległe z tyldą nad"] # 0x29e4 - - "⧥": [t: "identyczne i ukośne równoległe"] # 0x29e5 - - "⧦": [t: "równoliczne"] # 0x29e6 - - "⧧": [t: "termodynamiczny"] # 0x29e7 - - "⧨": [t: "trójkąt skierowany w dół z lewą połową czarną"] # 0x29e8 - - "⧩": [t: "trójkąt skierowany w dół z prawą połową czarną"] # 0x29e9 - - "⧪": [t: "czarny romb ze strzałką w dół"] # 0x29ea - - "⧫": [t: "czarny romb"] # 0x29eb - - "⧬": [t: "białe koło ze strzałką w dół"] # 0x29ec - - "⧭": [t: "czarne koło ze strzałką w dół"] # 0x29ed - - "⧮": [t: "biały kwadrat z przekreśleniem błędu"] # 0x29ee - - "⧯": [t: "czarny kwadrat z przekreśleniem błędu"] # 0x29ef - - "⧰": [t: "biały romb z przekreśleniem błędu"] # 0x29f0 - - "⧱": [t: "czarny romb z przekreśleniem błędu"] # 0x29f1 - - "⧲": [t: "białe koło z przekreśleniem błędu"] # 0x29f2 - - "⧳": [t: "czarne koło z przekreśleniem błędu"] # 0x29f3 - - "⧴": [t: "opóźniona reguła"] # 0x29f4 - - "⧵": [t: "operator ukośnika odwrotnego"] # 0x29f5 - - "⧶": [t: "ukośnik z kreską nad"] # 0x29f6 - - "⧷": [t: "ukośnik odwrotny z poziomą kreską"] # 0x29f7 - - "⧸": [t: "duży ukośnik"] # 0x29f8 - - "⧹": [t: "duży ukośnik odwrotny"] # 0x29f9 - - "⧺": [t: "podwójny plus"] # 0x29fa - - "⧻": [t: "potrójny plus"] # 0x29fb - - "⧼": [t: "lewy zakrzywiony nawias kątowy"] # 0x29fc - - "⧽": [t: "prawy zakrzywiony nawias kątowy"] # 0x29fd - - "⧾": [t: "maleńki"] # 0x29fe - - "⧿": [t: "mini"] # 0x29ff - - "⨀": [t: "operator kropka w kółku"] # 0x2a00 - - "⨁": [t: "operator plus w kółku"] # 0x2a01 - - "⨂": [t: "operator razy w kółku"] # 0x2a02 - - "⨃": [t: "operator sumy zbiorów z kropką"] # 0x2a03 - - "⨄": [t: "operator sumy zbiorów z plusem"] # 0x2a04 - - "⨅": [t: "operator kwadratowej części wspólnej"] # 0x2a05 - - "⨆": [t: "operator kwadratowej sumy zbiorów"] # 0x2a06 - - "⨇": [t: "operator podwójnej koniunkcji logicznej"] # 0x2a07 - - "⨈": [t: "dwa logiczne lub operator"] # 0x2a08 - - "⨉": [t: "razy operator"] # 0x2a09 - - "⨊": [t: "suma modulo dwa"] # 0x2a0a - - "⨋": [t: "suma z całką"] # 0x2a0b - - "⨌": [t: "operator poczwórnej całki"] # 0x2a0c - - "⨍": [t: "całka części skończonej"] # 0x2a0d - - "⨎": [t: "całka z podwójnym przekreśleniem"] # 0x2a0e - - "⨏": [t: "całka uśredniona z ukośnikiem"] # 0x2a0f - - "⨐": [t: "funkcja cyrkulacji"] # 0x2a10 - - "⨑": [t: "całka przeciwna do ruchu wskazówek zegara"] # 0x2a11 - - "⨒": [t: "całka liniowa po prostokątnej ścieżce wokół bieguna"] # 0x2a12 - - "⨓": [t: "całka liniowa po półokrągłej ścieżce wokół bieguna"] # 0x2a13 - - "⨔": [t: "całka liniowa bez pola"] # 0x2a14 - - "⨕": [t: "operator całki wokół punktu"] # 0x2a15 - - "⨖": [t: "operator całki kwaternionowej"] # 0x2a16 - - "⨗": [t: "całka ze strzałką w lewo z haczykiem"] # 0x2a17 - - "⨘": [t: "całka ze znakiem razy"] # 0x2a18 - - "⨙": [t: "całka z częścią wspólną"] # 0x2a19 - - "⨚": [t: "całka z sumą zbiorów"] # 0x2a1a - - "⨛": [t: "całka z kreską nad"] # 0x2a1b - - "⨜": [t: "całka z podkreśleniem"] # 0x2a1c - - "⨝": [t: "złączenie"] # 0x2a1d - - "⨞": [t: "duży lewy trójkąt operator"] # 0x2a1e - - "⨟": [t: "składanie schematów w notacji Z"] # 0x2a1f - - "⨠": [t: "potok schematów w notacji Z"] # 0x2a20 - - "⨡": [t: "rzutowanie schematów w notacji Z"] # 0x2a21 - - "⨢": [t: "znak plus z okręgiem nad"] # 0x2a22 - - "⨣": [t: "znak plus z daszkiem nad"] # 0x2a23 - - "⨤": [t: "znak plus z tyldą nad"] # 0x2a24 - - "⨥": [t: "znak plus z kropką pod"] # 0x2a25 - - "⨦": [t: "znak plus z tyldą pod"] # 0x2a26 - - "⨧": [t: "znak plus z indeksem dolnym dwa"] # 0x2a27 - - "⨨": [t: "znak plus z czarnym trójkątem"] # 0x2a28 - - "⨩": [t: "znak minus z przecinkiem nad"] # 0x2a29 - - "⨪": [t: "znak minus z kropką pod"] # 0x2a2a - - "⨫": [t: "znak minus z kropkami opadającymi"] # 0x2a2b - - "⨬": [t: "znak minus z kropkami wznoszącymi"] # 0x2a2c - - "⨭": [t: "znak plus w lewej połówce okręgu"] # 0x2a2d - - "⨮": [t: "znak plus w prawej połówce okręgu"] # 0x2a2e + - T: "otwórz nadzbiór" + - "⟅": [T: "lewy ogranicznik workowy w kształcie S"] # 0x27c5 + - "⟆": [T: "prawy ogranicznik workowy w kształcie S"] # 0x27c6 + - "⟇": [T: "lub z kropką wewnątrz"] # 0x27c7 + - "⟈": [T: "odwrotny ukośnik poprzedzający podzbiór"] # 0x27c8 + - "⟉": [T: "nadzbiór poprzedzający ukośnik"] # 0x27c9 + - "⟊": [T: "pionowa kreska z poziomą kreską"] # 0x27ca + - "⟋": [T: "matematyczny ukośnik wznoszący"] # 0x27cb + - "⟌": [T: "dzielenie pisemne"] # 0x27cc + - "⟍": [T: "matematyczny ukośnik opadający"] # 0x27cd + - "⟎": [T: "kwadratowa koniunkcja logiczna"] # 0x27ce + - "⟏": [T: "do kwadratu logiczne lub"] # 0x27cf + - "⟐": [T: "biały romb z wyśrodkowaną kropką"] # 0x27d0 + - "⟑": [T: "i z kropką"] # 0x27d1 + - "⟒": [T: "należy do z otwarciem u góry"] # 0x27d2 + - "⟓": [T: "prawy dolny narożnik z kropką"] # 0x27d3 + - "⟔": [T: "górny lewy narożnik z kropką"] # 0x27d4 + - "⟕": [T: "lewe złączenie zewnętrzne"] # 0x27d5 + - "⟖": [T: "prawe złączenie zewnętrzne"] # 0x27d6 + - "⟗": [T: "pełne złączenie zewnętrzne"] # 0x27d7 + - "⟘": [T: "duży górny znacznik"] # 0x27d8 + - "⟙": [T: "duży dolny znacznik"] # 0x27d9 + - "⟚": [T: "lewy i prawy podwójny znacznik dedukcji"] # 0x27da + - "⟛": [T: "lewy i prawy znacznik"] # 0x27db + - "⟜": [T: "lewa multimapa"] # 0x27dc + - "⟝": [T: "długi prawy znacznik"] # 0x27dd + - "⟞": [T: "długi lewy znacznik"] # 0x27de + - "⟟": [T: "górny znacznik z okręgiem nad"] # 0x27df + - "⟠": [T: "romb podzielony poziomą kreską"] # 0x27e0 + - "⟡": [T: "biały romb o wklęsłych bokach"] # 0x27e1 + - "⟢": [T: "biały romb o wklęsłych bokach z lewym haczykiem"] # 0x27e2 + - "⟣": [T: "biały romb o wklęsłych bokach z prawym haczykiem"] # 0x27e3 + - "⟤": [T: "biały kwadrat z lewym haczykiem"] # 0x27e4 + - "⟥": [T: "biały kwadrat z prawym haczykiem"] # 0x27e5 + - "⟦": [T: "lewy biały nawias kwadratowy"] # 0x27e6 + - "⟧": [T: "prawy biały nawias kwadratowy"] # 0x27e7 + - "⟨": [T: "lewy nawias kątowy"] # 0x27e8 + - "⟩": [T: "prawy nawias kątowy"] # 0x27e9 + - "⟪": [T: "lewy podwójny nawias kątowy"] # 0x27ea + - "⟫": [T: "prawy podwójny nawias kątowy"] # 0x27eb + - "⟬": [T: "lewy biały nawias skorupowy"] # 0x27ec + - "⟭": [T: "prawy biały nawias skorupowy"] # 0x27ed + - "⟮": [T: "lewy spłaszczony nawias"] # 0x27ee + - "⟯": [T: "prawy spłaszczony nawias"] # 0x27ef + - "⟰": [T: "poczwórna strzałka w górę"] # 0x27f0 + - "⟱": [T: "poczwórna strzałka w dół"] # 0x27f1 + - "⟲": [T: "okrężna strzałka z przerwą przeciwnie do ruchu wskazówek zegara"] # 0x27f2 + - "⟳": [T: "okrężna strzałka z przerwą zgodnie z ruchem wskazówek zegara"] # 0x27f3 + - "⟴": [T: "strzałka w prawo z plusem w kółku"] # 0x27f4 + - "⟵": [T: "długa strzałka w lewo"] # 0x27f5 + - "⟶": [T: "długa strzałka w prawo"] # 0x27f6 + - "⟷": [T: "długa strzałka w lewo i w prawo"] # 0x27f7 + - "⟸": [T: "długa podwójna strzałka w lewo"] # 0x27f8 + - "⟹": [T: "implikuje"] # 0x27f9 + - "⟺": [T: "wtedy i tylko wtedy"] # 0x27fa + - "⟻": [T: "długa strzałka w lewo od kreski"] # 0x27fb + - "⟼": [T: "długa strzałka w prawo od kreski"] # 0x27fc + - "⟽": [T: "długa podwójna strzałka w lewo od kreski"] # 0x27fd + - "⟾": [T: "długa podwójna strzałka w prawo od kreski"] # 0x27fe + - "⟿": [T: "długa falista strzałka w prawo"] # 0x27ff + - "⤀": [T: "strzałka w prawo z dwoma grotami i pionowym przekreśleniem"] # 0x2900 + - "⤁": [T: "strzałka w prawo z dwoma grotami i podwójnym pionowym przekreśleniem"] # 0x2901 + - "⤂": [T: "podwójna strzałka w lewo z pionową kreską"] # 0x2902 + - "⤃": [T: "podwójna strzałka w prawo z pionową kreską"] # 0x2903 + - "⤄": [T: "podwójna strzałka w lewo i w prawo z pionową kreską"] # 0x2904 + - "⤅": [T: "strzałka w prawo z dwoma grotami od kreski"] # 0x2905 + - "⤆": [T: "podwójna strzałka w lewo od kreski"] # 0x2906 + - "⤇": [T: "podwójna strzałka w prawo od kreski"] # 0x2907 + - "⤈": [T: "strzałka w dół z poziomą kreską"] # 0x2908 + - "⤉": [T: "strzałka w górę z poziomą kreską"] # 0x2909 + - "⤊": [T: "górny potrójny strzałka"] # 0x290a + - "⤋": [T: "dolny potrójny strzałka"] # 0x290b + - "⤌": [T: "lewy podwójny kreska strzałka"] # 0x290c + - "⤍": [T: "prawy podwójny kreska strzałka"] # 0x290d + - "⤎": [T: "lewy potrójny kreska strzałka"] # 0x290e + - "⤏": [T: "prawy potrójny kreska strzałka"] # 0x290f + - "⤐": [T: "strzałka w prawo z dwoma grotami i potrójną kreską"] # 0x2910 + - "⤑": [T: "strzałka w prawo z kropkowanym trzonem"] # 0x2911 + - "⤒": [T: "strzałka w górę do kreski"] # 0x2912 + - "⤓": [T: "strzałka w dół do kreski"] # 0x2913 + - "⤔": [T: "strzałka w prawo z ogonem i pionową kreską"] # 0x2914 + - "⤕": [T: "strzałka w prawo z ogonem i podwójną pionową kreską"] # 0x2915 + - "⤖": [T: "strzałka w prawo z dwoma grotami i ogonem"] # 0x2916 + - "⤗": [T: "strzałka w prawo z dwoma grotami, ogonem i pionowym przekreśleniem"] # 0x2917 + - "⤘": [T: "strzałka w prawo z dwoma grotami, ogonem i podwójnym pionowym przekreśleniem"] # 0x2918 + - "⤙": [T: "strzałka w lewo ogon"] # 0x2919 + - "⤚": [T: "strzałka w prawo ogon"] # 0x291a + - "⤛": [T: "lewy podwójna strzałka ogon"] # 0x291b + - "⤜": [T: "prawy podwójna strzałka ogon"] # 0x291c + - "⤝": [T: "strzałka w lewo do wypełnionego rombu"] # 0x291d + - "⤞": [T: "strzałka w prawo do wypełnionego rombu"] # 0x291e + - "⤟": [T: "strzałka w lewo od kreski do wypełnionego rombu"] # 0x291f + - "⤠": [T: "strzałka w prawo od kreski do wypełnionego rombu"] # 0x2920 + - "⤡": [T: "strzałka północno-zachodnia i południowo-wschodnia"] # 0x2921 + - "⤢": [T: "strzałka północno-wschodnia i południowo-zachodnia"] # 0x2922 + - "⤣": [T: "strzałka północno-zachodnia z hakiem"] # 0x2923 + - "⤤": [T: "strzałka północno-wschodnia z hakiem"] # 0x2924 + - "⤥": [T: "strzałka południowo-wschodnia z hakiem"] # 0x2925 + - "⤦": [T: "strzałka południowo-zachodnia z hakiem"] # 0x2926 + - "⤧": [T: "strzałka północno-zachodnia i strzałka północno-wschodnia"] # 0x2927 + - "⤨": [T: "strzałka północno-wschodnia i strzałka południowo-wschodnia"] # 0x2928 + - "⤩": [T: "strzałka południowo-wschodnia i strzałka południowo-zachodnia"] # 0x2929 + - "⤪": [T: "strzałka południowo-zachodnia i strzałka północno-zachodnia"] # 0x292a + - "⤫": [T: "ukośna wznosząca przecinająca ukośną opadającą"] # 0x292b + - "⤬": [T: "ukośna opadająca przecinająca ukośną wznoszącą"] # 0x292c + - "⤭": [T: "strzałka południowo-wschodnia przecinająca strzałkę północno-wschodnią"] # 0x292d + - "⤮": [T: "strzałka północno-wschodnia przecinająca strzałkę południowo-wschodnią"] # 0x292e + - "⤯": [T: "ukośna opadająca przecinająca strzałkę północno-wschodnią"] # 0x292f + - "⤰": [T: "ukośna wznosząca przecinająca strzałkę południowo-wschodnią"] # 0x2930 + - "⤱": [T: "strzałka północno-wschodnia przecinająca strzałkę północno-zachodnią"] # 0x2931 + - "⤲": [T: "strzałka północno-zachodnia przecinająca strzałkę północno-wschodnią"] # 0x2932 + - "⤳": [T: "falista strzałka skierowana bezpośrednio w prawo"] # 0x2933 + - "⤴": [T: "strzałka skierowana w prawo, potem zakrzywiona w górę"] # 0x2934 + - "⤵": [T: "strzałka skierowana w prawo, potem zakrzywiona w dół"] # 0x2935 + - "⤶": [T: "strzałka skierowana w dół, potem zakrzywiona w lewo"] # 0x2936 + - "⤷": [T: "strzałka skierowana w dół, potem zakrzywiona w prawo"] # 0x2937 + - "⤸": [T: "prawostronna strzałka łukowa zgodna z ruchem wskazówek zegara"] # 0x2938 + - "⤹": [T: "lewostronna strzałka łukowa przeciwna do ruchu wskazówek zegara"] # 0x2939 + - "⤺": [T: "górna strzałka łukowa przeciwna do ruchu wskazówek zegara"] # 0x293a + - "⤻": [T: "dolna strzałka łukowa przeciwna do ruchu wskazówek zegara"] # 0x293b + - "⤼": [T: "górna strzałka łukowa zgodna z ruchem wskazówek zegara z minusem"] # 0x293c + - "⤽": [T: "górna strzałka łukowa przeciwna do ruchu wskazówek zegara z plusem"] # 0x293d + - "⤾": [T: "dolna prawa strzałka półokrągła zgodna z ruchem wskazówek zegara"] # 0x293e + - "⤿": [T: "dolna lewa strzałka półokrągła przeciwna do ruchu wskazówek zegara"] # 0x293f + - "⥀": [T: "zamknięta strzałka okrężna przeciwna do ruchu wskazówek zegara"] # 0x2940 + - "⥁": [T: "zamknięta strzałka okrężna zgodna z ruchem wskazówek zegara"] # 0x2941 + - "⥂": [T: "strzałka w prawo nad krótką strzałką w lewo"] # 0x2942 + - "⥃": [T: "strzałka w lewo nad krótką strzałką w prawo"] # 0x2943 + - "⥄": [T: "krótka strzałka w prawo nad strzałką w lewo"] # 0x2944 + - "⥅": [T: "strzałka w prawo z plusem pod spodem"] # 0x2945 + - "⥆": [T: "strzałka w lewo z plusem pod spodem"] # 0x2946 + - "⥇": [T: "strzałka w prawo przez X"] # 0x2947 + - "⥈": [T: "strzałka w lewo i w prawo przez okrąg"] # 0x2948 + - "⥉": [T: "strzałka w górę z dwoma grotami od okręgu"] # 0x2949 + - "⥊": [T: "lewy grot górny prawy grot dolny harpun"] # 0x294a + - "⥋": [T: "lewy grot dolny prawy grot górny harpun"] # 0x294b + - "⥌": [T: "górny grot prawy dolny grot lewy harpun"] # 0x294c + - "⥍": [T: "górny grot lewy dolny grot prawy harpun"] # 0x294d + - "⥎": [T: "lewy grot górny prawy grot górny harpun"] # 0x294e + - "⥏": [T: "górny grot prawy dolny grot prawy harpun"] # 0x294f + - "⥐": [T: "lewy grot dolny prawy grot dolny harpun"] # 0x2950 + - "⥑": [T: "górny grot lewy dolny grot lewy harpun"] # 0x2951 + - "⥒": [T: "lewy harpun z grotem górnym do kreski"] # 0x2952 + - "⥓": [T: "prawy harpun z grotem górnym do kreski"] # 0x2953 + - "⥔": [T: "górny harpun z grotem prawym do kreski"] # 0x2954 + - "⥕": [T: "dolny harpun z grotem prawym do kreski"] # 0x2955 + - "⥖": [T: "lewy harpun z grotem dolnym do kreski"] # 0x2956 + - "⥗": [T: "prawy harpun z grotem dolnym do kreski"] # 0x2957 + - "⥘": [T: "górny harpun z grotem lewym do kreski"] # 0x2958 + - "⥙": [T: "dolny harpun z grotem lewym do kreski"] # 0x2959 + - "⥚": [T: "lewy harpun z grotem górnym od kreski"] # 0x295a + - "⥛": [T: "prawy harpun z grotem górnym od kreski"] # 0x295b + - "⥜": [T: "górny harpun z grotem prawym od kreski"] # 0x295c + - "⥝": [T: "dolny harpun z grotem prawym od kreski"] # 0x295d + - "⥞": [T: "lewy harpun z grotem dolnym od kreski"] # 0x295e + - "⥟": [T: "prawy harpun z grotem dolnym od kreski"] # 0x295f + - "⥠": [T: "górny harpun z grotem lewym od kreski"] # 0x2960 + - "⥡": [T: "dolny harpun z grotem lewym od kreski"] # 0x2961 + - "⥢": [T: "lewy harpun z grotem górnym nad lewy harpun z grotem dolnym"] # 0x2962 + - "⥣": [T: "górny harpun z grotem w lewo obok górnego harpuna z grotem w prawo"] # 0x2963 + - "⥤": [T: "prawy harpun z grotem górnym nad prawy harpun z grotem dolnym"] # 0x2964 + - "⥥": [T: "dolny harpun z grotem w lewo obok dolnego harpuna z grotem w prawo"] # 0x2965 + - "⥦": [T: "lewy harpun z grotem górnym nad prawy harpun z grotem górnym"] # 0x2966 + - "⥧": [T: "lewy harpun z grotem dolnym nad prawy harpun z grotem dolnym"] # 0x2967 + - "⥨": [T: "prawy harpun z grotem górnym nad lewy harpun z grotem górnym"] # 0x2968 + - "⥩": [T: "prawy harpun z grotem dolnym nad lewy harpun z grotem dolnym"] # 0x2969 + - "⥪": [T: "lewy harpun z grotem górnym nad długą kreską"] # 0x296a + - "⥫": [T: "lewy harpun z grotem dolnym pod długą kreską"] # 0x296b + - "⥬": [T: "prawy harpun z grotem górnym nad długą kreską"] # 0x296c + - "⥭": [T: "prawy harpun z grotem dolnym pod długą kreską"] # 0x296d + - "⥮": [T: "górny harpun z grotem w lewo obok dolnego harpuna z grotem w prawo"] # 0x296e + - "⥯": [T: "dolny harpun z grotem w lewo obok górnego harpuna z grotem w prawo"] # 0x296f + - "⥰": [T: "podwójna strzałka w prawo z zaokrąglonym grotem"] # 0x2970 + - "⥱": [T: "równa się nad strzałka w prawo"] # 0x2971 + - "⥲": [T: "operator tyldy nad strzałką w prawo"] # 0x2972 + - "⥳": [T: "strzałka w lewo nad operatorem tyldy"] # 0x2973 + - "⥴": [T: "strzałka w prawo nad operatorem tyldy"] # 0x2974 + - "⥵": [T: "strzałka w prawo nad znakiem przybliżonej równości"] # 0x2975 + - "⥶": [T: "mniejsze niż nad strzałka w lewo"] # 0x2976 + - "⥷": [T: "strzałka w lewo przez znak mniejsze niż"] # 0x2977 + - "⥸": [T: "większe niż nad strzałka w prawo"] # 0x2978 + - "⥹": [T: "podzbiór nad strzałka w prawo"] # 0x2979 + - "⥺": [T: "strzałka w lewo przez znak podzbioru"] # 0x297a + - "⥻": [T: "nadzbiór nad strzałka w lewo"] # 0x297b + - "⥼": [T: "lewy rybi ogon"] # 0x297c + - "⥽": [T: "prawy rybi ogon"] # 0x297d + - "⥾": [T: "górny rybi ogon"] # 0x297e + - "⥿": [T: "dolny rybi ogon"] # 0x297f + - "⦀": [T: "potrójna pionowa kreska jako ogranicznik"] # 0x2980 + - "⦁": [T: "punkt w notacji Z"] # 0x2981 + - "⦂": [T: "dwukropek typu w notacji Z"] # 0x2982 + - "⦃": [T: "lewy biały nawias klamrowy"] # 0x2983 + - "⦄": [T: "prawy biały nawias klamrowy"] # 0x2984 + - "⦅": [T: "lewy biały nawias"] # 0x2985 + - "⦆": [T: "prawy biały nawias"] # 0x2986 + - "⦇": [T: "lewy nawias kwadratowy obrazu w notacji Z"] # 0x2987 + - "⦈": [T: "prawy nawias kwadratowy obrazu w notacji Z"] # 0x2988 + - "⦉": [T: "lewy nawias kwadratowy wiązania w notacji Z"] # 0x2989 + - "⦊": [T: "prawy nawias kwadratowy wiązania w notacji Z"] # 0x298a + - "⦋": [T: "lewy nawias kwadratowy z podkreśleniem"] # 0x298b + - "⦌": [T: "prawy nawias kwadratowy z podkreśleniem"] # 0x298c + - "⦍": [T: "lewy nawias kwadratowy z haczykiem w górnym rogu"] # 0x298d + - "⦎": [T: "prawy nawias kwadratowy z haczykiem w dolnym rogu"] # 0x298e + - "⦏": [T: "lewy nawias kwadratowy z haczykiem w dolnym rogu"] # 0x298f + - "⦐": [T: "prawy nawias kwadratowy z haczykiem w górnym rogu"] # 0x2990 + - "⦑": [T: "lewy kąt nawias kwadratowy z kropką"] # 0x2991 + - "⦒": [T: "prawy nawias kątowy z kropką"] # 0x2992 + - "⦓": [T: "lewy łuk mniejsze niż nawias kwadratowy"] # 0x2993 + - "⦔": [T: "prawy łuk większe niż nawias kwadratowy"] # 0x2994 + - "⦕": [T: "podwójny lewy łuk większe niż nawias kwadratowy"] # 0x2995 + - "⦖": [T: "podwójny prawy łuk mniejsze niż nawias kwadratowy"] # 0x2996 + - "⦗": [T: "lewy czarny nawias skorupowy"] # 0x2997 + - "⦘": [T: "prawy czarny nawias skorupowy"] # 0x2998 + - "⦙": [T: "kropkowany ogranicznik"] # 0x2999 + - "⦚": [T: "pionowa linia zygzakowata"] # 0x299a + - "⦛": [T: "kąt mierzony otwarty w lewo"] # 0x299b + - "⦜": [T: "wariant kąta prostego z kwadratem"] # 0x299c + - "⦝": [T: "mierzony kąt prosty z kropką"] # 0x299d + - "⦞": [T: "kąt z s wewnątrz"] # 0x299e + - "⦟": [T: "akut kąt"] # 0x299f + - "⦠": [T: "kąt sferyczny otwarty w lewo"] # 0x29a0 + - "⦡": [T: "kąt sferyczny otwarty w górę"] # 0x29a1 + - "⦢": [T: "odwrócony kąt"] # 0x29a2 + - "⦣": [T: "kąt odwrócony"] # 0x29a3 + - "⦤": [T: "kąt z podkreśleniem"] # 0x29a4 + - "⦥": [T: "kąt odwrócony z podkreśleniem"] # 0x29a5 + - "⦦": [T: "kąt ukośny otwarty w górę"] # 0x29a6 + - "⦧": [T: "kąt ukośny otwarty w dół"] # 0x29a7 + - "⦨": [T: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w górę i w prawo"] # 0x29a8 + - "⦩": [T: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w górę i w lewo"] # 0x29a9 + - "⦪": [T: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w dół i w prawo"] # 0x29aa + - "⦫": [T: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w dół i w lewo"] # 0x29ab + - "⦬": [T: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w prawo i w górę"] # 0x29ac + - "⦭": [T: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w lewo i w górę"] # 0x29ad + - "⦮": [T: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w prawo i w dół"] # 0x29ae + - "⦯": [T: "kąt mierzony z otwartym ramieniem zakończonym strzałką skierowaną w lewo i w dół"] # 0x29af + - "⦰": [T: "odwrócony zbiór pusty"] # 0x29b0 + - "⦱": [T: "zbiór pusty z kreską nad"] # 0x29b1 + - "⦲": [T: "zbiór pusty z małym okręgiem nad"] # 0x29b2 + - "⦳": [T: "zbiór pusty ze strzałką w prawo nad"] # 0x29b3 + - "⦴": [T: "zbiór pusty ze strzałką w lewo nad"] # 0x29b4 + - "⦵": [T: "okrąg z kreską poziomą"] # 0x29b5 + - "⦶": [T: "pionowa kreska w kółku"] # 0x29b6 + - "⦷": [T: "w kółku równoległe"] # 0x29b7 + - "⦸": [T: "ukośnik odwrotny w kółku"] # 0x29b8 + - "⦹": [T: "w kółku prostopadłe"] # 0x29b9 + - "⦺": [T: "okrąg podzielony kreską poziomą, z górną połową podzieloną kreską pionową"] # 0x29ba + - "⦻": [T: "okrąg z nałożonym X"] # 0x29bb + - "⦼": [T: "znak dzielenia w kółku obrócony przeciwnie do ruchu wskazówek zegara"] # 0x29bc + - "⦽": [T: "strzałka w górę przez okrąg"] # 0x29bd + - "⦾": [T: "w kółku biały punkt"] # 0x29be + - "⦿": [T: "w kółku punkt"] # 0x29bf + - "⧀": [T: "w kółku mniejsze niż"] # 0x29c0 + - "⧁": [T: "w kółku większe niż"] # 0x29c1 + - "⧂": [T: "okrąg z małym okręgiem po prawej"] # 0x29c2 + - "⧃": [T: "okrąg z dwiema poziomymi kreskami po prawej"] # 0x29c3 + - "⧄": [T: "kwadrat z ukośnikiem wznoszącym"] # 0x29c4 + - "⧅": [T: "kwadrat z ukośnikiem opadającym"] # 0x29c5 + - "⧆": [T: "do kwadratu gwiazdka"] # 0x29c6 + - "⧇": [T: "do kwadratu mały okrąg"] # 0x29c7 + - "⧈": [T: "do kwadratu kwadrat"] # 0x29c8 + - "⧉": [T: "dwa połączone kwadraty"] # 0x29c9 + - "⧊": [T: "trójkąt z kropką nad"] # 0x29ca + - "⧋": [T: "trójkąt z podkreśleniem"] # 0x29cb + - "⧌": [T: "s w trójkąt"] # 0x29cc + - "⧍": [T: "trójkąt z szeryfami u dołu"] # 0x29cd + - "⧎": [T: "prawy trójkąt nad lewy trójkąt"] # 0x29ce + - "⧏": [T: "lewy trójkąt obok pionowej kreski"] # 0x29cf + - "⧐": [T: "pionowa kreska obok prawego trójkąta"] # 0x29d0 + - "⧑": [T: "kokarda z lewą połową czarną"] # 0x29d1 + - "⧒": [T: "kokarda z prawą połową czarną"] # 0x29d2 + - "⧓": [T: "czarna kokarda"] # 0x29d3 + - "⧔": [T: "znak mnożenia z lewą połową czarną"] # 0x29d4 + - "⧕": [T: "znak mnożenia z prawą połową czarną"] # 0x29d5 + - "⧖": [T: "biały klepsydra"] # 0x29d6 + - "⧗": [T: "czarny klepsydra"] # 0x29d7 + - "⧘": [T: "lewy falisty ogranicznik"] # 0x29d8 + - "⧙": [T: "prawy falisty ogranicznik"] # 0x29d9 + - "⧚": [T: "lewy podwójny falisty ogranicznik"] # 0x29da + - "⧛": [T: "prawy podwójny falisty ogranicznik"] # 0x29db + - "⧜": [T: "niepełna nieskończoność"] # 0x29dc + - "⧝": [T: "wiązanie nad nieskończonością"] # 0x29dd + - "⧞": [T: "zanegowana nieskończoność z kreską pionową"] # 0x29de + - "⧟": [T: "podwójnie zakończona strzałka mapująca"] # 0x29df + - "⧠": [T: "kwadrat z konturowym obrysem"] # 0x29e0 + - "⧡": [T: "wzrasta jak"] # 0x29e1 + - "⧢": [T: "iloczyn przetasowania"] # 0x29e2 + - "⧣": [T: "znak równości i ukośne równoległe"] # 0x29e3 + - "⧤": [T: "znak równości i ukośne równoległe z tyldą nad"] # 0x29e4 + - "⧥": [T: "identyczne i ukośne równoległe"] # 0x29e5 + - "⧦": [T: "równoliczne"] # 0x29e6 + - "⧧": [T: "termodynamiczny"] # 0x29e7 + - "⧨": [T: "trójkąt skierowany w dół z lewą połową czarną"] # 0x29e8 + - "⧩": [T: "trójkąt skierowany w dół z prawą połową czarną"] # 0x29e9 + - "⧪": [T: "czarny romb ze strzałką w dół"] # 0x29ea + - "⧫": [T: "czarny romb"] # 0x29eb + - "⧬": [T: "białe koło ze strzałką w dół"] # 0x29ec + - "⧭": [T: "czarne koło ze strzałką w dół"] # 0x29ed + - "⧮": [T: "biały kwadrat z przekreśleniem błędu"] # 0x29ee + - "⧯": [T: "czarny kwadrat z przekreśleniem błędu"] # 0x29ef + - "⧰": [T: "biały romb z przekreśleniem błędu"] # 0x29f0 + - "⧱": [T: "czarny romb z przekreśleniem błędu"] # 0x29f1 + - "⧲": [T: "białe koło z przekreśleniem błędu"] # 0x29f2 + - "⧳": [T: "czarne koło z przekreśleniem błędu"] # 0x29f3 + - "⧴": [T: "opóźniona reguła"] # 0x29f4 + - "⧵": [T: "operator ukośnika odwrotnego"] # 0x29f5 + - "⧶": [T: "ukośnik z kreską nad"] # 0x29f6 + - "⧷": [T: "ukośnik odwrotny z poziomą kreską"] # 0x29f7 + - "⧸": [T: "duży ukośnik"] # 0x29f8 + - "⧹": [T: "duży ukośnik odwrotny"] # 0x29f9 + - "⧺": [T: "podwójny plus"] # 0x29fa + - "⧻": [T: "potrójny plus"] # 0x29fb + - "⧼": [T: "lewy zakrzywiony nawias kątowy"] # 0x29fc + - "⧽": [T: "prawy zakrzywiony nawias kątowy"] # 0x29fd + - "⧾": [T: "maleńki"] # 0x29fe + - "⧿": [T: "mini"] # 0x29ff + - "⨀": [T: "operator kropka w kółku"] # 0x2a00 + - "⨁": [T: "operator plus w kółku"] # 0x2a01 + - "⨂": [T: "operator razy w kółku"] # 0x2a02 + - "⨃": [T: "operator sumy zbiorów z kropką"] # 0x2a03 + - "⨄": [T: "operator sumy zbiorów z plusem"] # 0x2a04 + - "⨅": [T: "operator kwadratowej części wspólnej"] # 0x2a05 + - "⨆": [T: "operator kwadratowej sumy zbiorów"] # 0x2a06 + - "⨇": [T: "operator podwójnej koniunkcji logicznej"] # 0x2a07 + - "⨈": [T: "dwa logiczne lub operator"] # 0x2a08 + - "⨉": [T: "razy operator"] # 0x2a09 + - "⨊": [T: "suma modulo dwa"] # 0x2a0a + - "⨋": [T: "suma z całką"] # 0x2a0b + - "⨌": [T: "operator poczwórnej całki"] # 0x2a0c + - "⨍": [T: "całka części skończonej"] # 0x2a0d + - "⨎": [T: "całka z podwójnym przekreśleniem"] # 0x2a0e + - "⨏": [T: "całka uśredniona z ukośnikiem"] # 0x2a0f + - "⨐": [T: "funkcja cyrkulacji"] # 0x2a10 + - "⨑": [T: "całka przeciwna do ruchu wskazówek zegara"] # 0x2a11 + - "⨒": [T: "całka liniowa po prostokątnej ścieżce wokół bieguna"] # 0x2a12 + - "⨓": [T: "całka liniowa po półokrągłej ścieżce wokół bieguna"] # 0x2a13 + - "⨔": [T: "całka liniowa bez pola"] # 0x2a14 + - "⨕": [T: "operator całki wokół punktu"] # 0x2a15 + - "⨖": [T: "operator całki kwaternionowej"] # 0x2a16 + - "⨗": [T: "całka ze strzałką w lewo z haczykiem"] # 0x2a17 + - "⨘": [T: "całka ze znakiem razy"] # 0x2a18 + - "⨙": [T: "całka z częścią wspólną"] # 0x2a19 + - "⨚": [T: "całka z sumą zbiorów"] # 0x2a1a + - "⨛": [T: "całka z kreską nad"] # 0x2a1b + - "⨜": [T: "całka z podkreśleniem"] # 0x2a1c + - "⨝": [T: "złączenie"] # 0x2a1d + - "⨞": [T: "duży lewy trójkąt operator"] # 0x2a1e + - "⨟": [T: "składanie schematów w notacji Z"] # 0x2a1f + - "⨠": [T: "potok schematów w notacji Z"] # 0x2a20 + - "⨡": [T: "rzutowanie schematów w notacji Z"] # 0x2a21 + - "⨢": [T: "znak plus z okręgiem nad"] # 0x2a22 + - "⨣": [T: "znak plus z daszkiem nad"] # 0x2a23 + - "⨤": [T: "znak plus z tyldą nad"] # 0x2a24 + - "⨥": [T: "znak plus z kropką pod"] # 0x2a25 + - "⨦": [T: "znak plus z tyldą pod"] # 0x2a26 + - "⨧": [T: "znak plus z indeksem dolnym dwa"] # 0x2a27 + - "⨨": [T: "znak plus z czarnym trójkątem"] # 0x2a28 + - "⨩": [T: "znak minus z przecinkiem nad"] # 0x2a29 + - "⨪": [T: "znak minus z kropką pod"] # 0x2a2a + - "⨫": [T: "znak minus z kropkami opadającymi"] # 0x2a2b + - "⨬": [T: "znak minus z kropkami wznoszącymi"] # 0x2a2c + - "⨭": [T: "znak plus w lewej połówce okręgu"] # 0x2a2d + - "⨮": [T: "znak plus w prawej połówce okręgu"] # 0x2a2e - "⨯": # 0x2a2f - test: if: "$Verbosity='Terse'" - then: [t: "krzyż"] - else: [t: "krzyż iloczyn"] - - "⨰": [t: "znak mnożenia z kropką nad"] # 0x2a30 - - "⨱": [t: "znak mnożenia z podkreśleniem"] # 0x2a31 - - "⨲": [t: "iloczyn półprosty domknięty od dołu"] # 0x2a32 - - "⨳": [t: "iloczyn spłaszczony"] # 0x2a33 - - "⨴": [t: "znak mnożenia w lewej połówce okręgu"] # 0x2a34 - - "⨵": [t: "znak mnożenia w prawej połówce okręgu"] # 0x2a35 - - "⨶": [t: "znak mnożenia w kółku z daszkiem"] # 0x2a36 - - "⨷": [t: "znak mnożenia w podwójnym okręgu"] # 0x2a37 - - "⨸": [t: "znak dzielenia w kółku"] # 0x2a38 - - "⨹": [t: "znak plus w trójkącie"] # 0x2a39 - - "⨺": [t: "znak minus w trójkącie"] # 0x2a3a - - "⨻": [t: "znak mnożenia w trójkącie"] # 0x2a3b - - "⨼": [t: "iloczyn wewnętrzny"] # 0x2a3c - - "⨽": [t: "prawostronny iloczyn wewnętrzny"] # 0x2a3d - - "⨾": [t: "składanie relacji w notacji Z"] # 0x2a3e - - "⨿": [t: "amalgamacja lub koprodukt"] # 0x2a3f - - "⩀": [t: "część wspólna z kropką"] # 0x2a40 - - "⩁": [t: "suma zbiorów ze znakiem minus"] # 0x2a41 - - "⩂": [t: "suma zbiorów z kreską nad"] # 0x2a42 - - "⩃": [t: "część wspólna z kreską nad"] # 0x2a43 - - "⩄": [t: "część wspólna z koniunkcją logiczną"] # 0x2a44 - - "⩅": [t: "suma zbiorów z alternatywą logiczną"] # 0x2a45 - - "⩆": [t: "suma zbiorów nad część wspólna"] # 0x2a46 - - "⩇": [t: "część wspólna nad suma zbiorów"] # 0x2a47 - - "⩈": [t: "suma zbiorów nad kreska nad część wspólna"] # 0x2a48 - - "⩉": [t: "część wspólna nad kreska nad suma zbiorów"] # 0x2a49 - - "⩊": [t: "suma zbiorów obok sumy zbiorów połączona kreską"] # 0x2a4a - - "⩋": [t: "część wspólna obok części wspólnej połączona kreską"] # 0x2a4b - - "⩌": [t: "zamknięta suma zbiorów z szeryfami"] # 0x2a4c - - "⩍": [t: "zamknięta część wspólna z szeryfami"] # 0x2a4d - - "⩎": [t: "podwójny kwadrat część wspólna"] # 0x2a4e - - "⩏": [t: "podwójny kwadrat suma zbiorów"] # 0x2a4f - - "⩐": [t: "zamknięta suma zbiorów z szeryfami i iloczynem spłaszczonym"] # 0x2a50 - - "⩑": [t: "koniunkcja logiczna z kropką nad"] # 0x2a51 - - "⩒": [t: "logiczne lub z kropką nad"] # 0x2a52 - - "⩓": [t: "podwójna koniunkcja logiczna"] # 0x2a53 - - "⩔": [t: "podwójny logiczne lub"] # 0x2a54 - - "⩕": [t: "dwa przecinające się logiczne i"] # 0x2a55 - - "⩖": [t: "dwa przecinające się logiczne lub"] # 0x2a56 - - "⩗": [t: "skośne duże lub"] # 0x2a57 - - "⩘": [t: "skośne duże i"] # 0x2a58 - - "⩙": [t: "logiczne lub nakładające się na logiczne i"] # 0x2a59 - - "⩚": [t: "logiczne i ze środkowym trzonem"] # 0x2a5a - - "⩛": [t: "logiczne lub ze środkowym trzonem"] # 0x2a5b - - "⩜": [t: "koniunkcja logiczna z poziomą kreską"] # 0x2a5c - - "⩝": [t: "alternatywa logiczna z poziomą kreską"] # 0x2a5d - - "⩞": [t: "logiczne i z podwójną kreską nad"] # 0x2a5e - - "⩟": [t: "koniunkcja logiczna z podkreśleniem"] # 0x2a5f - - "⩠": [t: "logiczne i z podwójną kreską pod"] # 0x2a60 - - "⩡": [t: "małe logiczne lub z podkreśleniem"] # 0x2a61 - - "⩢": [t: "logiczne lub z podwójną kreską nad"] # 0x2a62 - - "⩣": [t: "logiczne lub z podwójną kreską pod"] # 0x2a63 - - "⩤": [t: "antyograniczenie dziedziny w notacji Z"] # 0x2a64 - - "⩥": [t: "antyograniczenie przeciwdziedziny w notacji Z"] # 0x2a65 - - "⩦": [t: "znak równości z kropką pod spodem"] # 0x2a66 - - "⩧": [t: "identyczne z kropką nad"] # 0x2a67 - - "⩨": [t: "potrójna kreska pozioma z podwójną pionową kreską"] # 0x2a68 - - "⩩": [t: "potrójna kreska pozioma z potrójną pionową kreską"] # 0x2a69 - - "⩪": [t: "operator tyldy z kropką nad"] # 0x2a6a - - "⩫": [t: "operator tyldy ze wznoszącymi kropkami"] # 0x2a6b - - "⩬": [t: "podobne minus podobne"] # 0x2a6c - - "⩭": [t: "przystające z kropką nad"] # 0x2a6d - - "⩮": [t: "równa się z gwiazdką"] # 0x2a6e - - "⩯": [t: "w przybliżeniu równe z daszkiem"] # 0x2a6f - - "⩰": [t: "w przybliżeniu równe lub równe"] # 0x2a70 - - "⩱": [t: "znak równości nad znakiem plus"] # 0x2a71 - - "⩲": [t: "znak plus nad znakiem równości"] # 0x2a72 - - "⩳": [t: "znak równości nad operatorem tyldy"] # 0x2a73 - - "⩴": [t: "podwójny dwukropek równe"] # 0x2a74 - - "⩵": [t: "dwa kolejne znaki równości"] # 0x2a75 - - "⩶": [t: "trzy kolejne znaki równości"] # 0x2a76 - - "⩷": [t: "znak równości z dwiema kropkami nad i dwiema kropkami pod"] # 0x2a77 - - "⩸": [t: "równoważne z czterema kropkami nad"] # 0x2a78 - - "⩹": [t: "mniejsze niż z okrąg wewnątrz"] # 0x2a79 - - "⩺": [t: "większe niż z okrąg wewnątrz"] # 0x2a7a - - "⩻": [t: "mniejsze niż ze znakiem zapytania nad"] # 0x2a7b - - "⩼": [t: "większe niż ze znakiem zapytania nad"] # 0x2a7c - - "⩽": [t: "mniejsze niż lub ukośnie równe"] # 0x2a7d - - "⩾": [t: "większe niż lub ukośnie równe"] # 0x2a7e - - "⩿": [t: "mniejsze niż lub ukośnie równe z kropką wewnątrz"] # 0x2a7f - - "⪀": [t: "większe niż lub ukośnie równe z kropką wewnątrz"] # 0x2a80 - - "⪁": [t: "mniejsze niż lub ukośnie równe z kropką nad"] # 0x2a81 - - "⪂": [t: "większe niż lub ukośnie równe z kropką nad"] # 0x2a82 - - "⪃": [t: "mniejsze niż lub ukośnie równe z kropką nad prawą stroną"] # 0x2a83 - - "⪄": [t: "większe niż lub ukośnie równe z kropką nad lewą stroną"] # 0x2a84 - - "⪅": [t: "mniejsze niż lub przybliżone"] # 0x2a85 - - "⪆": [t: "większe niż lub przybliżone"] # 0x2a86 - - "⪇": [t: "mniejsze niż z pojedynczą linią nierówności"] # 0x2a87 - - "⪈": [t: "większe niż z pojedynczą linią nierówności"] # 0x2a88 - - "⪉": [t: "mniejsze niż i nie przybliżone"] # 0x2a89 - - "⪊": [t: "większe niż i nie przybliżone"] # 0x2a8a - - "⪋": [t: "mniejsze niż nad podwójną linią równości nad większe niż"] # 0x2a8b - - "⪌": [t: "większe niż nad podwójną linią równości nad mniejsze niż"] # 0x2a8c - - "⪍": [t: "mniejsze niż nad podobne lub równe"] # 0x2a8d - - "⪎": [t: "większe niż nad podobne lub równe"] # 0x2a8e - - "⪏": [t: "mniejsze niż nad podobne nad większe niż"] # 0x2a8f - - "⪐": [t: "większe niż nad podobne nad mniejsze niż"] # 0x2a90 - - "⪑": [t: "mniejsze niż nad większe niż nad podwójną linią równości"] # 0x2a91 - - "⪒": [t: "większe niż nad mniejsze niż nad podwójną linią równości"] # 0x2a92 - - "⪓": [t: "mniejsze niż nad ukośnie równe nad większe niż nad ukośnie równe"] # 0x2a93 - - "⪔": [t: "większe niż nad ukośnie równe nad mniejsze niż nad ukośnie równe"] # 0x2a94 - - "⪕": [t: "ukośnie równe lub mniejsze niż"] # 0x2a95 - - "⪖": [t: "ukośnie równe lub większe niż"] # 0x2a96 - - "⪗": [t: "ukośnie równe lub mniejsze niż z kropką wewnątrz"] # 0x2a97 - - "⪘": [t: "ukośnie równe lub większe niż z kropką wewnątrz"] # 0x2a98 - - "⪙": [t: "podwójna linia równa lub mniejsze niż"] # 0x2a99 - - "⪚": [t: "podwójna linia równa lub większe niż"] # 0x2a9a - - "⪛": [t: "podwójna linia ukośnie równa lub mniejsze niż"] # 0x2a9b - - "⪜": [t: "podwójna linia ukośnie równa lub większe niż"] # 0x2a9c - - "⪝": [t: "podobne lub mniejsze niż"] # 0x2a9d - - "⪞": [t: "podobne lub większe niż"] # 0x2a9e - - "⪟": [t: "podobne nad mniejsze niż nad znakiem równości"] # 0x2a9f - - "⪠": [t: "podobne nad większe niż nad znakiem równości"] # 0x2aa0 - - "⪡": [t: "podwójnie zagnieżdżone mniejsze niż"] # 0x2aa1 - - "⪢": [t: "podwójnie zagnieżdżone większe niż"] # 0x2aa2 - - "⪣": [t: "podwójnie zagnieżdżone mniejsze niż z podkreśleniem"] # 0x2aa3 - - "⪤": [t: "większe niż nakładające się na mniejsze niż"] # 0x2aa4 - - "⪥": [t: "większe niż obok mniejsze niż"] # 0x2aa5 - - "⪦": [t: "mniejsze niż zamknięte krzywą"] # 0x2aa6 - - "⪧": [t: "większe niż zamknięte krzywą"] # 0x2aa7 - - "⪨": [t: "mniejsze niż zamknięte krzywą nad ukośnie równe"] # 0x2aa8 - - "⪩": [t: "większe niż zamknięte krzywą nad ukośnie równe"] # 0x2aa9 - - "⪪": [t: "mniejsze niż"] # 0x2aaa - - "⪫": [t: "większe niż"] # 0x2aab - - "⪬": [t: "mniejsze niż lub równe"] # 0x2aac - - "⪭": [t: "większe niż lub równe"] # 0x2aad - - "⪮": [t: "znak równości z garbem nad"] # 0x2aae - - "⪯": [t: "poprzedza nad pojedynczą linią równości"] # 0x2aaf - - "⪰": [t: "następuje po nad pojedynczą linią równości"] # 0x2ab0 - - "⪱": [t: "poprzedza nad pojedynczą linią nierówności"] # 0x2ab1 - - "⪲": [t: "następuje po nad pojedynczy linia nie równa się"] # 0x2ab2 - - "⪳": [t: "poprzedza nad znakiem równości"] # 0x2ab3 - - "⪴": [t: "następuje po nad znakiem równości"] # 0x2ab4 - - "⪵": [t: "poprzedza nad nie równa się"] # 0x2ab5 - - "⪶": [t: "następuje po nad nie równa się"] # 0x2ab6 - - "⪷": [t: "poprzedza nad znakiem przybliżonej równości"] # 0x2ab7 - - "⪸": [t: "następuje po nad znakiem przybliżonej równości"] # 0x2ab8 - - "⪹": [t: "poprzedza nad znakiem nie w przybliżeniu równe"] # 0x2ab9 - - "⪺": [t: "następuje po nad znakiem nie w przybliżeniu równe"] # 0x2aba - - "⪻": [t: "podwójny poprzedza"] # 0x2abb - - "⪼": [t: "podwójny następuje po"] # 0x2abc - - "⪽": [t: "podzbiór z kropką"] # 0x2abd - - "⪾": [t: "nadzbiór z kropką"] # 0x2abe - - "⪿": [t: "podzbiór ze znakiem plus pod spodem"] # 0x2abf - - "⫀": [t: "nadzbiór ze znakiem plus pod spodem"] # 0x2ac0 - - "⫁": [t: "podzbiór ze znakiem mnożenia pod"] # 0x2ac1 - - "⫂": [t: "nadzbiór ze znakiem mnożenia pod"] # 0x2ac2 - - "⫃": [t: "podzbiór lub równy z kropką nad"] # 0x2ac3 - - "⫄": [t: "nadzbiór lub równy z kropką nad"] # 0x2ac4 - - "⫅": [t: "podzbiór nad znakiem równości"] # 0x2ac5 - - "⫆": [t: "nadzbiór nad znakiem równości"] # 0x2ac6 - - "⫇": [t: "podzbiór nad operatorem tyldy"] # 0x2ac7 - - "⫈": [t: "nadzbiór nad operatorem tyldy"] # 0x2ac8 - - "⫉": [t: "podzbiór nad znakiem przybliżonej równości"] # 0x2ac9 - - "⫊": [t: "nadzbiór nad znakiem przybliżonej równości"] # 0x2aca - - "⫋": [t: "podzbiór nad nie równa się"] # 0x2acb - - "⫌": [t: "nadzbiór nad nie równa się"] # 0x2acc - - "⫍": [t: "kwadrat lewy otwórz ramka operator"] # 0x2acd - - "⫎": [t: "kwadrat prawy otwórz ramka operator"] # 0x2ace - - "⫏": [t: "zamknięty podzbiór"] # 0x2acf - - "⫐": [t: "zamknięty nadzbiór"] # 0x2ad0 - - "⫑": [t: "zamknięty podzbiór lub równe"] # 0x2ad1 - - "⫒": [t: "zamknięty nadzbiór lub równe"] # 0x2ad2 - - "⫓": [t: "podzbiór nad nadzbiór"] # 0x2ad3 - - "⫔": [t: "nadzbiór nad podzbiór"] # 0x2ad4 - - "⫕": [t: "podzbiór nad podzbiór"] # 0x2ad5 - - "⫖": [t: "nadzbiór nad nadzbiór"] # 0x2ad6 - - "⫗": [t: "nadzbiór obok podzbioru"] # 0x2ad7 - - "⫘": [t: "nadzbiór obok podzbioru połączony kreską"] # 0x2ad8 - - "⫙": [t: "należy do otwarte od dołu"] # 0x2ad9 - - "⫚": [t: "widły z górną poprzeczką"] # 0x2ada - - "⫛": [t: "poprzeczna część wspólna"] # 0x2adb - - "⫝̸": [t: "rozwidlanie"] # 0x2adc - - "⫝": [t: "nierozwidlanie"] # 0x2add - - "⫞": [t: "krótki lewy znacznik"] # 0x2ade - - "⫟": [t: "krótki dolny znacznik"] # 0x2adf - - "⫠": [t: "krótki górny znacznik"] # 0x2ae0 - - "⫡": [t: "prostopadłe z literą s"] # 0x2ae1 - - "⫢": [t: "pionowa kreska z potrójnym prawym znacznikiem dedukcji"] # 0x2ae2 - - "⫣": [t: "podwójna pionowa kreska z lewym znacznikiem dedukcji"] # 0x2ae3 - - "⫤": [t: "pionowa kreska z podwójnym lewym znacznikiem dedukcji"] # 0x2ae4 - - "⫥": [t: "podwójna pionowa kreska z podwójnym lewym znacznikiem dedukcji"] # 0x2ae5 - - "⫦": [t: "długa kreska od lewego elementu z podwójną pionową kreską"] # 0x2ae6 - - "⫧": [t: "krótki dolny znacznik z kreską nad"] # 0x2ae7 - - "⫨": [t: "krótki górny znacznik z podkreśleniem"] # 0x2ae8 - - "⫩": [t: "krótki górny znacznik nad krótkim dolnym znacznikiem"] # 0x2ae9 - - "⫪": [t: "podwójny dolny znacznik"] # 0x2aea - - "⫫": [t: "podwójny górny znacznik"] # 0x2aeb - - "⫬": [t: "znak negacji z podwójnym przekreśleniem"] # 0x2aec - - "⫭": [t: "odwrócony znak negacji z podwójnym przekreśleniem"] # 0x2aed - - "⫮": [t: "nie dzieli z odwróconym ukośnikiem negacji"] # 0x2aee - - "⫯": [t: "pionowa linia z okręgiem nad"] # 0x2aef - - "⫰": [t: "pionowa linia z okręgiem pod"] # 0x2af0 - - "⫱": [t: "dolny znacznik z okręgiem pod"] # 0x2af1 - - "⫲": [t: "równoległe z poziomą kreską"] # 0x2af2 - - "⫳": [t: "równoległe z operatorem tyldy"] # 0x2af3 - - "⫴": [t: "potrójna pionowa kreska jako relacja binarna"] # 0x2af4 - - "⫵": [t: "potrójna pionowa kreska z poziomą kreską"] # 0x2af5 - - "⫶": [t: "potrójny dwukropek operator"] # 0x2af6 - - "⫷": [t: "potrójnie zagnieżdżone mniejsze niż"] # 0x2af7 - - "⫸": [t: "potrójnie zagnieżdżone większe niż"] # 0x2af8 - - "⫹": [t: "podwójna linia ukośnie mniejsze lub równe"] # 0x2af9 - - "⫺": [t: "podwójna linia ukośnie większe lub równe"] # 0x2afa - - "⫻": [t: "potrójny ukośnik jako relacja binarna"] # 0x2afb - - "⫼": [t: "operator dużej potrójnej pionowej kreski"] # 0x2afc - - "⫽": [t: "podwójny ukośnik operator"] # 0x2afd - - "⫾": [t: "biała pionowa kreska"] # 0x2afe - - "⫿": [t: "biała pionowa kreska"] # 0x2aff - - "⬀": [t: "biała strzałka na północny wschód"] # 0x2b00 - - "⬁": [t: "biała strzałka na północny zachód"] # 0x2b01 - - "⬂": [t: "biała strzałka na południowy wschód"] # 0x2b02 - - "⬃": [t: "biała strzałka na południowy zachód"] # 0x2b03 - - "⬄": [t: "biała strzałka w lewo i w prawo"] # 0x2b04 - - "⬅": [t: "lewy czarny strzałka"] # 0x2b05 - - "⬆": [t: "górny czarny strzałka"] # 0x2b06 - - "⬇": [t: "dolny czarny strzałka"] # 0x2b07 - - "⬈": [t: "północ wschód czarny strzałka"] # 0x2b08 - - "⬉": [t: "północ zachód czarny strzałka"] # 0x2b09 - - "⬊": [t: "południe wschód czarny strzałka"] # 0x2b0a - - "⬋": [t: "południe zachód czarny strzałka"] # 0x2b0b - - "⬌": [t: "lewy prawy czarny strzałka"] # 0x2b0c - - "⬍": [t: "górny dolny czarny strzałka"] # 0x2b0d - - "⬎": [t: "strzałka w prawo z dolnym czubkiem"] # 0x2b0e - - "⬏": [t: "strzałka w prawo z górnym czubkiem"] # 0x2b0f - - "⬐": [t: "strzałka w lewo z dolnym czubkiem"] # 0x2b10 - - "⬑": [t: "strzałka w lewo z górnym czubkiem"] # 0x2b11 - - "⬒": [t: "kwadrat z górną połową czarną"] # 0x2b12 - - "⬓": [t: "kwadrat z dolną połową czarną"] # 0x2b13 - - "⬔": [t: "kwadrat z górną prawą ukośną połową czarną"] # 0x2b14 - - "⬕": [t: "kwadrat z dolną lewą ukośną połową czarną"] # 0x2b15 - - "⬖": [t: "romb z lewą połową czarną"] # 0x2b16 - - "⬗": [t: "romb z prawą połową czarną"] # 0x2b17 - - "⬘": [t: "romb z górną połową czarną"] # 0x2b18 - - "⬙": [t: "romb z dolną połową czarną"] # 0x2b19 - - "⬚": [t: "ramka"] # 0x2b1a - - "⬛": [t: "czarny duży kwadrat"] # 0x2b1b - - "⬜": [t: "biały duży kwadrat"] # 0x2b1c - - "⬝": [t: "czarny bardzo mały kwadrat"] # 0x2b1d - - "⬞": [t: "biały bardzo mały kwadrat"] # 0x2b1e - - "⬟": [t: "czarny pięciokąt"] # 0x2b1f - - "⬠": [t: "biały pięciokąt"] # 0x2b20 - - "⬡": [t: "biały sześciokąt"] # 0x2b21 - - "⬢": [t: "czarny sześciokąt"] # 0x2b22 - - "⬣": [t: "poziomy czarny sześciokąt"] # 0x2b23 - - "⬤": [t: "czarny duży okrąg"] # 0x2b24 - - "⬥": [t: "czarny średni romb"] # 0x2b25 - - "⬦": [t: "biały średni romb"] # 0x2b26 - - "⬧": [t: "czarny średni romb"] # 0x2b27 - - "⬨": [t: "biały średni romb"] # 0x2b28 - - "⬩": [t: "czarny mały romb"] # 0x2b29 - - "⬪": [t: "czarny mały romb"] # 0x2b2a - - "⬫": [t: "biały mały romb"] # 0x2b2b - - "⬬": [t: "czarna pozioma elipsa"] # 0x2b2c - - "⬭": [t: "biała pozioma elipsa"] # 0x2b2d - - "⬮": [t: "czarna pionowa elipsa"] # 0x2b2e - - "⬯": [t: "biała pionowa elipsa"] # 0x2b2f - - "⬰": [t: "strzałka w lewo z małym okręgiem"] # 0x2b30 - - "⬱": [t: "trzy strzałki w lewo"] # 0x2b31 - - "⬲": [t: "strzałka w lewo z plusem w kółku"] # 0x2b32 - - "⬳": [t: "długa falista strzałka w lewo"] # 0x2b33 - - "⬴": [t: "strzałka w lewo z dwoma grotami i pionowym przekreśleniem"] # 0x2b34 - - "⬵": [t: "strzałka w lewo z dwoma grotami i podwójnym pionowym przekreśleniem"] # 0x2b35 - - "⬶": [t: "strzałka w lewo z dwoma grotami od kreski"] # 0x2b36 - - "⬷": [t: "strzałka w lewo z dwoma grotami i potrójną kreską"] # 0x2b37 - - "⬸": [t: "strzałka w lewo z kropkowanym trzonem"] # 0x2b38 - - "⬹": [t: "strzałka w lewo z ogonem i pionową kreską"] # 0x2b39 - - "⬺": [t: "strzałka w lewo z ogonem i podwójną pionową kreską"] # 0x2b3a - - "⬻": [t: "strzałka w lewo z dwoma grotami i ogonem"] # 0x2b3b - - "⬼": [t: "strzałka w lewo z dwoma grotami, ogonem i pionowym przekreśleniem"] # 0x2b3c - - "⬽": [t: "strzałka w lewo z dwoma grotami, ogonem i podwójnym pionowym przekreśleniem"] # 0x2b3d - - "⬾": [t: "strzałka w lewo przez X"] # 0x2b3e - - "⬿": [t: "falista strzałka skierowana bezpośrednio w lewo"] # 0x2b3f - - "⭀": [t: "znak równości nad strzałką w lewo"] # 0x2b40 - - "⭁": [t: "operator odwrotnej tyldy nad strzałką w lewo"] # 0x2b41 - - "⭂": [t: "strzałka w lewo nad odwróconym znakiem przybliżonej równości"] # 0x2b42 - - "⭃": [t: "strzałka w prawo przez znak większe niż"] # 0x2b43 - - "⭄": [t: "strzałka w prawo przez znak nadzbioru"] # 0x2b44 - - "⭅": [t: "lewy poczwórny strzałka"] # 0x2b45 - - "⭆": [t: "prawy poczwórny strzałka"] # 0x2b46 - - "⭇": [t: "operator odwrotnej tyldy nad strzałką w prawo"] # 0x2b47 - - "⭈": [t: "strzałka w prawo nad odwróconym znakiem przybliżonej równości"] # 0x2b48 - - "⭉": [t: "operator tyldy nad strzałką w lewo"] # 0x2b49 - - "⭊": [t: "strzałka w lewo nad znakiem przybliżonej równości"] # 0x2b4a - - "⭋": [t: "strzałka w lewo nad operatorem odwrotnej tyldy"] # 0x2b4b - - "⭌": [t: "strzałka w prawo nad operatorem odwrotnej tyldy"] # 0x2b4c - - "⭐": [t: "biały średni gwiazda"] # 0x2b50 - - "⭑": [t: "czarny mały gwiazda"] # 0x2b51 - - "⭒": [t: "biały mały gwiazda"] # 0x2b52 - - "⭓": [t: "czarny prawy skierowany pięciokąt"] # 0x2b53 - - "⭔": [t: "biały prawy skierowany pięciokąt"] # 0x2b54 - - "⭕": [t: "gruby duży okrąg"] # 0x2b55 - - "⭖": [t: "gruby owal z owalem wewnątrz"] # 0x2b56 - - "⭗": [t: "gruby okrąg z okręgiem wewnątrz"] # 0x2b57 - - "⭘": [t: "gruby okrąg"] # 0x2b58 - - "⭙": [t: "gruby ukośny krzyż w kółku"] # 0x2b59 - - "⸀": [t: "kąt prosty znacznik podstawienia"] # 0x2e00 - - "⸁": [t: "kąt prosty kropkowany znacznik podstawienia"] # 0x2e01 - - "⸂": [t: "lewy nawias kwadratowy podstawienia"] # 0x2e02 - - "⸃": [t: "prawy nawias kwadratowy podstawienia"] # 0x2e03 - - "⸄": [t: "lewy kropkowany nawias kwadratowy podstawienia"] # 0x2e04 - - "⸅": [t: "prawy kropkowany nawias kwadratowy podstawienia"] # 0x2e05 - - "⸆": [t: "podniesiony znacznik interpolacji"] # 0x2e06 - - "⸇": [t: "podniesiony kropkowany znacznik interpolacji"] # 0x2e07 - - "⸈": [t: "kropkowany znacznik transpozycji"] # 0x2e08 - - "⸉": [t: "lewy nawias kwadratowy transpozycji"] # 0x2e09 - - "⸊": [t: "prawy nawias kwadratowy transpozycji"] # 0x2e0a - - "⸋": [t: "podniesiony kwadrat"] # 0x2e0b - - "⸌": [t: "lewy podniesiony pominięcia nawias kwadratowy"] # 0x2e0c - - "⸍": [t: "prawy podniesiony pominięcia nawias kwadratowy"] # 0x2e0d - - "⸎": [t: "redakcyjny koronis"] # 0x2e0e + then: [T: "krzyż"] + else: [T: "krzyż iloczyn"] + - "⨰": [T: "znak mnożenia z kropką nad"] # 0x2a30 + - "⨱": [T: "znak mnożenia z podkreśleniem"] # 0x2a31 + - "⨲": [T: "iloczyn półprosty domknięty od dołu"] # 0x2a32 + - "⨳": [T: "iloczyn spłaszczony"] # 0x2a33 + - "⨴": [T: "znak mnożenia w lewej połówce okręgu"] # 0x2a34 + - "⨵": [T: "znak mnożenia w prawej połówce okręgu"] # 0x2a35 + - "⨶": [T: "znak mnożenia w kółku z daszkiem"] # 0x2a36 + - "⨷": [T: "znak mnożenia w podwójnym okręgu"] # 0x2a37 + - "⨸": [T: "znak dzielenia w kółku"] # 0x2a38 + - "⨹": [T: "znak plus w trójkącie"] # 0x2a39 + - "⨺": [T: "znak minus w trójkącie"] # 0x2a3a + - "⨻": [T: "znak mnożenia w trójkącie"] # 0x2a3b + - "⨼": [T: "iloczyn wewnętrzny"] # 0x2a3c + - "⨽": [T: "prawostronny iloczyn wewnętrzny"] # 0x2a3d + - "⨾": [T: "składanie relacji w notacji Z"] # 0x2a3e + - "⨿": [T: "amalgamacja lub koprodukt"] # 0x2a3f + - "⩀": [T: "część wspólna z kropką"] # 0x2a40 + - "⩁": [T: "suma zbiorów ze znakiem minus"] # 0x2a41 + - "⩂": [T: "suma zbiorów z kreską nad"] # 0x2a42 + - "⩃": [T: "część wspólna z kreską nad"] # 0x2a43 + - "⩄": [T: "część wspólna z koniunkcją logiczną"] # 0x2a44 + - "⩅": [T: "suma zbiorów z alternatywą logiczną"] # 0x2a45 + - "⩆": [T: "suma zbiorów nad część wspólna"] # 0x2a46 + - "⩇": [T: "część wspólna nad suma zbiorów"] # 0x2a47 + - "⩈": [T: "suma zbiorów nad kreska nad część wspólna"] # 0x2a48 + - "⩉": [T: "część wspólna nad kreska nad suma zbiorów"] # 0x2a49 + - "⩊": [T: "suma zbiorów obok sumy zbiorów połączona kreską"] # 0x2a4a + - "⩋": [T: "część wspólna obok części wspólnej połączona kreską"] # 0x2a4b + - "⩌": [T: "zamknięta suma zbiorów z szeryfami"] # 0x2a4c + - "⩍": [T: "zamknięta część wspólna z szeryfami"] # 0x2a4d + - "⩎": [T: "podwójny kwadrat część wspólna"] # 0x2a4e + - "⩏": [T: "podwójny kwadrat suma zbiorów"] # 0x2a4f + - "⩐": [T: "zamknięta suma zbiorów z szeryfami i iloczynem spłaszczonym"] # 0x2a50 + - "⩑": [T: "koniunkcja logiczna z kropką nad"] # 0x2a51 + - "⩒": [T: "logiczne lub z kropką nad"] # 0x2a52 + - "⩓": [T: "podwójna koniunkcja logiczna"] # 0x2a53 + - "⩔": [T: "podwójny logiczne lub"] # 0x2a54 + - "⩕": [T: "dwa przecinające się logiczne i"] # 0x2a55 + - "⩖": [T: "dwa przecinające się logiczne lub"] # 0x2a56 + - "⩗": [T: "skośne duże lub"] # 0x2a57 + - "⩘": [T: "skośne duże i"] # 0x2a58 + - "⩙": [T: "logiczne lub nakładające się na logiczne i"] # 0x2a59 + - "⩚": [T: "logiczne i ze środkowym trzonem"] # 0x2a5a + - "⩛": [T: "logiczne lub ze środkowym trzonem"] # 0x2a5b + - "⩜": [T: "koniunkcja logiczna z poziomą kreską"] # 0x2a5c + - "⩝": [T: "alternatywa logiczna z poziomą kreską"] # 0x2a5d + - "⩞": [T: "logiczne i z podwójną kreską nad"] # 0x2a5e + - "⩟": [T: "koniunkcja logiczna z podkreśleniem"] # 0x2a5f + - "⩠": [T: "logiczne i z podwójną kreską pod"] # 0x2a60 + - "⩡": [T: "małe logiczne lub z podkreśleniem"] # 0x2a61 + - "⩢": [T: "logiczne lub z podwójną kreską nad"] # 0x2a62 + - "⩣": [T: "logiczne lub z podwójną kreską pod"] # 0x2a63 + - "⩤": [T: "antyograniczenie dziedziny w notacji Z"] # 0x2a64 + - "⩥": [T: "antyograniczenie przeciwdziedziny w notacji Z"] # 0x2a65 + - "⩦": [T: "znak równości z kropką pod spodem"] # 0x2a66 + - "⩧": [T: "identyczne z kropką nad"] # 0x2a67 + - "⩨": [T: "potrójna kreska pozioma z podwójną pionową kreską"] # 0x2a68 + - "⩩": [T: "potrójna kreska pozioma z potrójną pionową kreską"] # 0x2a69 + - "⩪": [T: "operator tyldy z kropką nad"] # 0x2a6a + - "⩫": [T: "operator tyldy ze wznoszącymi kropkami"] # 0x2a6b + - "⩬": [T: "podobne minus podobne"] # 0x2a6c + - "⩭": [T: "przystające z kropką nad"] # 0x2a6d + - "⩮": [T: "równa się z gwiazdką"] # 0x2a6e + - "⩯": [T: "w przybliżeniu równe z daszkiem"] # 0x2a6f + - "⩰": [T: "w przybliżeniu równe lub równe"] # 0x2a70 + - "⩱": [T: "znak równości nad znakiem plus"] # 0x2a71 + - "⩲": [T: "znak plus nad znakiem równości"] # 0x2a72 + - "⩳": [T: "znak równości nad operatorem tyldy"] # 0x2a73 + - "⩴": [T: "podwójny dwukropek równe"] # 0x2a74 + - "⩵": [T: "dwa kolejne znaki równości"] # 0x2a75 + - "⩶": [T: "trzy kolejne znaki równości"] # 0x2a76 + - "⩷": [T: "znak równości z dwiema kropkami nad i dwiema kropkami pod"] # 0x2a77 + - "⩸": [T: "równoważne z czterema kropkami nad"] # 0x2a78 + - "⩹": [T: "mniejsze niż z okrąg wewnątrz"] # 0x2a79 + - "⩺": [T: "większe niż z okrąg wewnątrz"] # 0x2a7a + - "⩻": [T: "mniejsze niż ze znakiem zapytania nad"] # 0x2a7b + - "⩼": [T: "większe niż ze znakiem zapytania nad"] # 0x2a7c + - "⩽": [T: "mniejsze niż lub ukośnie równe"] # 0x2a7d + - "⩾": [T: "większe niż lub ukośnie równe"] # 0x2a7e + - "⩿": [T: "mniejsze niż lub ukośnie równe z kropką wewnątrz"] # 0x2a7f + - "⪀": [T: "większe niż lub ukośnie równe z kropką wewnątrz"] # 0x2a80 + - "⪁": [T: "mniejsze niż lub ukośnie równe z kropką nad"] # 0x2a81 + - "⪂": [T: "większe niż lub ukośnie równe z kropką nad"] # 0x2a82 + - "⪃": [T: "mniejsze niż lub ukośnie równe z kropką nad prawą stroną"] # 0x2a83 + - "⪄": [T: "większe niż lub ukośnie równe z kropką nad lewą stroną"] # 0x2a84 + - "⪅": [T: "mniejsze niż lub przybliżone"] # 0x2a85 + - "⪆": [T: "większe niż lub przybliżone"] # 0x2a86 + - "⪇": [T: "mniejsze niż z pojedynczą linią nierówności"] # 0x2a87 + - "⪈": [T: "większe niż z pojedynczą linią nierówności"] # 0x2a88 + - "⪉": [T: "mniejsze niż i nie przybliżone"] # 0x2a89 + - "⪊": [T: "większe niż i nie przybliżone"] # 0x2a8a + - "⪋": [T: "mniejsze niż nad podwójną linią równości nad większe niż"] # 0x2a8b + - "⪌": [T: "większe niż nad podwójną linią równości nad mniejsze niż"] # 0x2a8c + - "⪍": [T: "mniejsze niż nad podobne lub równe"] # 0x2a8d + - "⪎": [T: "większe niż nad podobne lub równe"] # 0x2a8e + - "⪏": [T: "mniejsze niż nad podobne nad większe niż"] # 0x2a8f + - "⪐": [T: "większe niż nad podobne nad mniejsze niż"] # 0x2a90 + - "⪑": [T: "mniejsze niż nad większe niż nad podwójną linią równości"] # 0x2a91 + - "⪒": [T: "większe niż nad mniejsze niż nad podwójną linią równości"] # 0x2a92 + - "⪓": [T: "mniejsze niż nad ukośnie równe nad większe niż nad ukośnie równe"] # 0x2a93 + - "⪔": [T: "większe niż nad ukośnie równe nad mniejsze niż nad ukośnie równe"] # 0x2a94 + - "⪕": [T: "ukośnie równe lub mniejsze niż"] # 0x2a95 + - "⪖": [T: "ukośnie równe lub większe niż"] # 0x2a96 + - "⪗": [T: "ukośnie równe lub mniejsze niż z kropką wewnątrz"] # 0x2a97 + - "⪘": [T: "ukośnie równe lub większe niż z kropką wewnątrz"] # 0x2a98 + - "⪙": [T: "podwójna linia równa lub mniejsze niż"] # 0x2a99 + - "⪚": [T: "podwójna linia równa lub większe niż"] # 0x2a9a + - "⪛": [T: "podwójna linia ukośnie równa lub mniejsze niż"] # 0x2a9b + - "⪜": [T: "podwójna linia ukośnie równa lub większe niż"] # 0x2a9c + - "⪝": [T: "podobne lub mniejsze niż"] # 0x2a9d + - "⪞": [T: "podobne lub większe niż"] # 0x2a9e + - "⪟": [T: "podobne nad mniejsze niż nad znakiem równości"] # 0x2a9f + - "⪠": [T: "podobne nad większe niż nad znakiem równości"] # 0x2aa0 + - "⪡": [T: "podwójnie zagnieżdżone mniejsze niż"] # 0x2aa1 + - "⪢": [T: "podwójnie zagnieżdżone większe niż"] # 0x2aa2 + - "⪣": [T: "podwójnie zagnieżdżone mniejsze niż z podkreśleniem"] # 0x2aa3 + - "⪤": [T: "większe niż nakładające się na mniejsze niż"] # 0x2aa4 + - "⪥": [T: "większe niż obok mniejsze niż"] # 0x2aa5 + - "⪦": [T: "mniejsze niż zamknięte krzywą"] # 0x2aa6 + - "⪧": [T: "większe niż zamknięte krzywą"] # 0x2aa7 + - "⪨": [T: "mniejsze niż zamknięte krzywą nad ukośnie równe"] # 0x2aa8 + - "⪩": [T: "większe niż zamknięte krzywą nad ukośnie równe"] # 0x2aa9 + - "⪪": [T: "mniejsze niż"] # 0x2aaa + - "⪫": [T: "większe niż"] # 0x2aab + - "⪬": [T: "mniejsze niż lub równe"] # 0x2aac + - "⪭": [T: "większe niż lub równe"] # 0x2aad + - "⪮": [T: "znak równości z garbem nad"] # 0x2aae + - "⪯": [T: "poprzedza nad pojedynczą linią równości"] # 0x2aaf + - "⪰": [T: "następuje po nad pojedynczą linią równości"] # 0x2ab0 + - "⪱": [T: "poprzedza nad pojedynczą linią nierówności"] # 0x2ab1 + - "⪲": [T: "następuje po nad pojedynczy linia nie równa się"] # 0x2ab2 + - "⪳": [T: "poprzedza nad znakiem równości"] # 0x2ab3 + - "⪴": [T: "następuje po nad znakiem równości"] # 0x2ab4 + - "⪵": [T: "poprzedza nad nie równa się"] # 0x2ab5 + - "⪶": [T: "następuje po nad nie równa się"] # 0x2ab6 + - "⪷": [T: "poprzedza nad znakiem przybliżonej równości"] # 0x2ab7 + - "⪸": [T: "następuje po nad znakiem przybliżonej równości"] # 0x2ab8 + - "⪹": [T: "poprzedza nad znakiem nie w przybliżeniu równe"] # 0x2ab9 + - "⪺": [T: "następuje po nad znakiem nie w przybliżeniu równe"] # 0x2aba + - "⪻": [T: "podwójny poprzedza"] # 0x2abb + - "⪼": [T: "podwójny następuje po"] # 0x2abc + - "⪽": [T: "podzbiór z kropką"] # 0x2abd + - "⪾": [T: "nadzbiór z kropką"] # 0x2abe + - "⪿": [T: "podzbiór ze znakiem plus pod spodem"] # 0x2abf + - "⫀": [T: "nadzbiór ze znakiem plus pod spodem"] # 0x2ac0 + - "⫁": [T: "podzbiór ze znakiem mnożenia pod"] # 0x2ac1 + - "⫂": [T: "nadzbiór ze znakiem mnożenia pod"] # 0x2ac2 + - "⫃": [T: "podzbiór lub równy z kropką nad"] # 0x2ac3 + - "⫄": [T: "nadzbiór lub równy z kropką nad"] # 0x2ac4 + - "⫅": [T: "podzbiór nad znakiem równości"] # 0x2ac5 + - "⫆": [T: "nadzbiór nad znakiem równości"] # 0x2ac6 + - "⫇": [T: "podzbiór nad operatorem tyldy"] # 0x2ac7 + - "⫈": [T: "nadzbiór nad operatorem tyldy"] # 0x2ac8 + - "⫉": [T: "podzbiór nad znakiem przybliżonej równości"] # 0x2ac9 + - "⫊": [T: "nadzbiór nad znakiem przybliżonej równości"] # 0x2aca + - "⫋": [T: "podzbiór nad nie równa się"] # 0x2acb + - "⫌": [T: "nadzbiór nad nie równa się"] # 0x2acc + - "⫍": [T: "kwadrat lewy otwórz ramka operator"] # 0x2acd + - "⫎": [T: "kwadrat prawy otwórz ramka operator"] # 0x2ace + - "⫏": [T: "zamknięty podzbiór"] # 0x2acf + - "⫐": [T: "zamknięty nadzbiór"] # 0x2ad0 + - "⫑": [T: "zamknięty podzbiór lub równe"] # 0x2ad1 + - "⫒": [T: "zamknięty nadzbiór lub równe"] # 0x2ad2 + - "⫓": [T: "podzbiór nad nadzbiór"] # 0x2ad3 + - "⫔": [T: "nadzbiór nad podzbiór"] # 0x2ad4 + - "⫕": [T: "podzbiór nad podzbiór"] # 0x2ad5 + - "⫖": [T: "nadzbiór nad nadzbiór"] # 0x2ad6 + - "⫗": [T: "nadzbiór obok podzbioru"] # 0x2ad7 + - "⫘": [T: "nadzbiór obok podzbioru połączony kreską"] # 0x2ad8 + - "⫙": [T: "należy do otwarte od dołu"] # 0x2ad9 + - "⫚": [T: "widły z górną poprzeczką"] # 0x2ada + - "⫛": [T: "poprzeczna część wspólna"] # 0x2adb + - "⫝̸": [T: "rozwidlanie"] # 0x2adc + - "⫝": [T: "nierozwidlanie"] # 0x2add + - "⫞": [T: "krótki lewy znacznik"] # 0x2ade + - "⫟": [T: "krótki dolny znacznik"] # 0x2adf + - "⫠": [T: "krótki górny znacznik"] # 0x2ae0 + - "⫡": [T: "prostopadłe z literą s"] # 0x2ae1 + - "⫢": [T: "pionowa kreska z potrójnym prawym znacznikiem dedukcji"] # 0x2ae2 + - "⫣": [T: "podwójna pionowa kreska z lewym znacznikiem dedukcji"] # 0x2ae3 + - "⫤": [T: "pionowa kreska z podwójnym lewym znacznikiem dedukcji"] # 0x2ae4 + - "⫥": [T: "podwójna pionowa kreska z podwójnym lewym znacznikiem dedukcji"] # 0x2ae5 + - "⫦": [T: "długa kreska od lewego elementu z podwójną pionową kreską"] # 0x2ae6 + - "⫧": [T: "krótki dolny znacznik z kreską nad"] # 0x2ae7 + - "⫨": [T: "krótki górny znacznik z podkreśleniem"] # 0x2ae8 + - "⫩": [T: "krótki górny znacznik nad krótkim dolnym znacznikiem"] # 0x2ae9 + - "⫪": [T: "podwójny dolny znacznik"] # 0x2aea + - "⫫": [T: "podwójny górny znacznik"] # 0x2aeb + - "⫬": [T: "znak negacji z podwójnym przekreśleniem"] # 0x2aec + - "⫭": [T: "odwrócony znak negacji z podwójnym przekreśleniem"] # 0x2aed + - "⫮": [T: "nie dzieli z odwróconym ukośnikiem negacji"] # 0x2aee + - "⫯": [T: "pionowa linia z okręgiem nad"] # 0x2aef + - "⫰": [T: "pionowa linia z okręgiem pod"] # 0x2af0 + - "⫱": [T: "dolny znacznik z okręgiem pod"] # 0x2af1 + - "⫲": [T: "równoległe z poziomą kreską"] # 0x2af2 + - "⫳": [T: "równoległe z operatorem tyldy"] # 0x2af3 + - "⫴": [T: "potrójna pionowa kreska jako relacja binarna"] # 0x2af4 + - "⫵": [T: "potrójna pionowa kreska z poziomą kreską"] # 0x2af5 + - "⫶": [T: "potrójny dwukropek operator"] # 0x2af6 + - "⫷": [T: "potrójnie zagnieżdżone mniejsze niż"] # 0x2af7 + - "⫸": [T: "potrójnie zagnieżdżone większe niż"] # 0x2af8 + - "⫹": [T: "podwójna linia ukośnie mniejsze lub równe"] # 0x2af9 + - "⫺": [T: "podwójna linia ukośnie większe lub równe"] # 0x2afa + - "⫻": [T: "potrójny ukośnik jako relacja binarna"] # 0x2afb + - "⫼": [T: "operator dużej potrójnej pionowej kreski"] # 0x2afc + - "⫽": [T: "podwójny ukośnik operator"] # 0x2afd + - "⫾": [T: "biała pionowa kreska"] # 0x2afe + - "⫿": [T: "biała pionowa kreska"] # 0x2aff + - "⬀": [T: "biała strzałka na północny wschód"] # 0x2b00 + - "⬁": [T: "biała strzałka na północny zachód"] # 0x2b01 + - "⬂": [T: "biała strzałka na południowy wschód"] # 0x2b02 + - "⬃": [T: "biała strzałka na południowy zachód"] # 0x2b03 + - "⬄": [T: "biała strzałka w lewo i w prawo"] # 0x2b04 + - "⬅": [T: "lewy czarny strzałka"] # 0x2b05 + - "⬆": [T: "górny czarny strzałka"] # 0x2b06 + - "⬇": [T: "dolny czarny strzałka"] # 0x2b07 + - "⬈": [T: "północ wschód czarny strzałka"] # 0x2b08 + - "⬉": [T: "północ zachód czarny strzałka"] # 0x2b09 + - "⬊": [T: "południe wschód czarny strzałka"] # 0x2b0a + - "⬋": [T: "południe zachód czarny strzałka"] # 0x2b0b + - "⬌": [T: "lewy prawy czarny strzałka"] # 0x2b0c + - "⬍": [T: "górny dolny czarny strzałka"] # 0x2b0d + - "⬎": [T: "strzałka w prawo z dolnym czubkiem"] # 0x2b0e + - "⬏": [T: "strzałka w prawo z górnym czubkiem"] # 0x2b0f + - "⬐": [T: "strzałka w lewo z dolnym czubkiem"] # 0x2b10 + - "⬑": [T: "strzałka w lewo z górnym czubkiem"] # 0x2b11 + - "⬒": [T: "kwadrat z górną połową czarną"] # 0x2b12 + - "⬓": [T: "kwadrat z dolną połową czarną"] # 0x2b13 + - "⬔": [T: "kwadrat z górną prawą ukośną połową czarną"] # 0x2b14 + - "⬕": [T: "kwadrat z dolną lewą ukośną połową czarną"] # 0x2b15 + - "⬖": [T: "romb z lewą połową czarną"] # 0x2b16 + - "⬗": [T: "romb z prawą połową czarną"] # 0x2b17 + - "⬘": [T: "romb z górną połową czarną"] # 0x2b18 + - "⬙": [T: "romb z dolną połową czarną"] # 0x2b19 + - "⬚": [T: "ramka"] # 0x2b1a + - "⬛": [T: "czarny duży kwadrat"] # 0x2b1b + - "⬜": [T: "biały duży kwadrat"] # 0x2b1c + - "⬝": [T: "czarny bardzo mały kwadrat"] # 0x2b1d + - "⬞": [T: "biały bardzo mały kwadrat"] # 0x2b1e + - "⬟": [T: "czarny pięciokąt"] # 0x2b1f + - "⬠": [T: "biały pięciokąt"] # 0x2b20 + - "⬡": [T: "biały sześciokąt"] # 0x2b21 + - "⬢": [T: "czarny sześciokąt"] # 0x2b22 + - "⬣": [T: "poziomy czarny sześciokąt"] # 0x2b23 + - "⬤": [T: "czarny duży okrąg"] # 0x2b24 + - "⬥": [T: "czarny średni romb"] # 0x2b25 + - "⬦": [T: "biały średni romb"] # 0x2b26 + - "⬧": [T: "czarny średni romb"] # 0x2b27 + - "⬨": [T: "biały średni romb"] # 0x2b28 + - "⬩": [T: "czarny mały romb"] # 0x2b29 + - "⬪": [T: "czarny mały romb"] # 0x2b2a + - "⬫": [T: "biały mały romb"] # 0x2b2b + - "⬬": [T: "czarna pozioma elipsa"] # 0x2b2c + - "⬭": [T: "biała pozioma elipsa"] # 0x2b2d + - "⬮": [T: "czarna pionowa elipsa"] # 0x2b2e + - "⬯": [T: "biała pionowa elipsa"] # 0x2b2f + - "⬰": [T: "strzałka w lewo z małym okręgiem"] # 0x2b30 + - "⬱": [T: "trzy strzałki w lewo"] # 0x2b31 + - "⬲": [T: "strzałka w lewo z plusem w kółku"] # 0x2b32 + - "⬳": [T: "długa falista strzałka w lewo"] # 0x2b33 + - "⬴": [T: "strzałka w lewo z dwoma grotami i pionowym przekreśleniem"] # 0x2b34 + - "⬵": [T: "strzałka w lewo z dwoma grotami i podwójnym pionowym przekreśleniem"] # 0x2b35 + - "⬶": [T: "strzałka w lewo z dwoma grotami od kreski"] # 0x2b36 + - "⬷": [T: "strzałka w lewo z dwoma grotami i potrójną kreską"] # 0x2b37 + - "⬸": [T: "strzałka w lewo z kropkowanym trzonem"] # 0x2b38 + - "⬹": [T: "strzałka w lewo z ogonem i pionową kreską"] # 0x2b39 + - "⬺": [T: "strzałka w lewo z ogonem i podwójną pionową kreską"] # 0x2b3a + - "⬻": [T: "strzałka w lewo z dwoma grotami i ogonem"] # 0x2b3b + - "⬼": [T: "strzałka w lewo z dwoma grotami, ogonem i pionowym przekreśleniem"] # 0x2b3c + - "⬽": [T: "strzałka w lewo z dwoma grotami, ogonem i podwójnym pionowym przekreśleniem"] # 0x2b3d + - "⬾": [T: "strzałka w lewo przez X"] # 0x2b3e + - "⬿": [T: "falista strzałka skierowana bezpośrednio w lewo"] # 0x2b3f + - "⭀": [T: "znak równości nad strzałką w lewo"] # 0x2b40 + - "⭁": [T: "operator odwrotnej tyldy nad strzałką w lewo"] # 0x2b41 + - "⭂": [T: "strzałka w lewo nad odwróconym znakiem przybliżonej równości"] # 0x2b42 + - "⭃": [T: "strzałka w prawo przez znak większe niż"] # 0x2b43 + - "⭄": [T: "strzałka w prawo przez znak nadzbioru"] # 0x2b44 + - "⭅": [T: "lewy poczwórny strzałka"] # 0x2b45 + - "⭆": [T: "prawy poczwórny strzałka"] # 0x2b46 + - "⭇": [T: "operator odwrotnej tyldy nad strzałką w prawo"] # 0x2b47 + - "⭈": [T: "strzałka w prawo nad odwróconym znakiem przybliżonej równości"] # 0x2b48 + - "⭉": [T: "operator tyldy nad strzałką w lewo"] # 0x2b49 + - "⭊": [T: "strzałka w lewo nad znakiem przybliżonej równości"] # 0x2b4a + - "⭋": [T: "strzałka w lewo nad operatorem odwrotnej tyldy"] # 0x2b4b + - "⭌": [T: "strzałka w prawo nad operatorem odwrotnej tyldy"] # 0x2b4c + - "⭐": [T: "biały średni gwiazda"] # 0x2b50 + - "⭑": [T: "czarny mały gwiazda"] # 0x2b51 + - "⭒": [T: "biały mały gwiazda"] # 0x2b52 + - "⭓": [T: "czarny prawy skierowany pięciokąt"] # 0x2b53 + - "⭔": [T: "biały prawy skierowany pięciokąt"] # 0x2b54 + - "⭕": [T: "gruby duży okrąg"] # 0x2b55 + - "⭖": [T: "gruby owal z owalem wewnątrz"] # 0x2b56 + - "⭗": [T: "gruby okrąg z okręgiem wewnątrz"] # 0x2b57 + - "⭘": [T: "gruby okrąg"] # 0x2b58 + - "⭙": [T: "gruby ukośny krzyż w kółku"] # 0x2b59 + - "⸀": [T: "kąt prosty znacznik podstawienia"] # 0x2e00 + - "⸁": [T: "kąt prosty kropkowany znacznik podstawienia"] # 0x2e01 + - "⸂": [T: "lewy nawias kwadratowy podstawienia"] # 0x2e02 + - "⸃": [T: "prawy nawias kwadratowy podstawienia"] # 0x2e03 + - "⸄": [T: "lewy kropkowany nawias kwadratowy podstawienia"] # 0x2e04 + - "⸅": [T: "prawy kropkowany nawias kwadratowy podstawienia"] # 0x2e05 + - "⸆": [T: "podniesiony znacznik interpolacji"] # 0x2e06 + - "⸇": [T: "podniesiony kropkowany znacznik interpolacji"] # 0x2e07 + - "⸈": [T: "kropkowany znacznik transpozycji"] # 0x2e08 + - "⸉": [T: "lewy nawias kwadratowy transpozycji"] # 0x2e09 + - "⸊": [T: "prawy nawias kwadratowy transpozycji"] # 0x2e0a + - "⸋": [T: "podniesiony kwadrat"] # 0x2e0b + - "⸌": [T: "lewy podniesiony pominięcia nawias kwadratowy"] # 0x2e0c + - "⸍": [T: "prawy podniesiony pominięcia nawias kwadratowy"] # 0x2e0d + - "⸎": [T: "redakcyjny koronis"] # 0x2e0e - "⸏": [T: "paragraphos"] # 0x2e0f - - "⸐": [t: "rozwidlony paragrafos"] # 0x2e10 - - "⸑": [t: "odwrócony rozwidlony paragrafos"] # 0x2e11 + - "⸐": [T: "rozwidlony paragrafos"] # 0x2e10 + - "⸑": [T: "odwrócony rozwidlony paragrafos"] # 0x2e11 - "⸒": [T: "hypodiastole"] # 0x2e12 - - "⸓": [t: "kropkowany obelos"] # 0x2e13 - - "⸔": [t: "dolny ancora"] # 0x2e14 - - "⸕": [t: "górny ancora"] # 0x2e15 - - "⸖": [t: "kropkowany kąt skierowany w prawo"] # 0x2e16 - - "⸗": [t: "podwójny ukośny łącznik"] # 0x2e17 - - "⸘": [t: "odwrócony interrobang"] # 0x2e18 - - "⸙": [t: "gałązka palmowa"] # 0x2e19 - - "⸚": [t: "łącznik z dierezą"] # 0x2e1a - - "⸛": [t: "tylda z pierścieniem nad"] # 0x2e1b - - "⸜": [t: "lewy dolny nawias kwadratowy parafrazy"] # 0x2e1c - - "⸝": [t: "prawy dolny nawias kwadratowy parafrazy"] # 0x2e1d - - "⸞": [t: "tylda z kropką nad"] # 0x2e1e - - "⸟": [t: "tylda z kropką pod"] # 0x2e1f - - "⸠": [t: "lewa pionowa kreska z piórem"] # 0x2e20 - - "⸡": [t: "prawa pionowa kreska z piórem"] # 0x2e21 - - "⸢": [t: "górny lewa połowa nawias kwadratowy"] # 0x2e22 - - "⸣": [t: "górny prawa połowa nawias kwadratowy"] # 0x2e23 - - "⸤": [t: "dolny lewa połowa nawias kwadratowy"] # 0x2e24 - - "⸥": [t: "dolny prawa połowa nawias kwadratowy"] # 0x2e25 - - "⸦": [t: "lewy obrócony nawias kwadratowy u"] # 0x2e26 - - "⸧": [t: "prawy obrócony nawias kwadratowy u"] # 0x2e27 - - "⸨": [t: "lewy podwójny nawias okrągły"] # 0x2e28 - - "⸩": [t: "prawy podwójny nawias okrągły"] # 0x2e29 - - "⸪": [t: "dwie kropki nad jedną kropką interpunkcyjną"] # 0x2e2a - - "⸫": [t: "jedna kropka nad dwiema kropkami interpunkcyjnymi"] # 0x2e2b - - "⸬": [t: "do kwadratu cztery kropka interpunkcyjny"] # 0x2e2c - - "⸭": [t: "pięć kropka znak"] # 0x2e2d - - "⸮": [t: "odwrócony zapytania znak"] # 0x2e2e - - "ⸯ": [t: "pionowy tylda"] # 0x2e2f - - "⸰": [t: "pierścień punkt"] # 0x2e30 - - "⸱": [t: "separator wyrazów środkowa kropka"] # 0x2e31 - - "⸲": [t: "odwrócony przecinek"] # 0x2e32 - - "⸳": [t: "podniesiony kropka"] # 0x2e33 - - "⸴": [t: "podniesiony przecinek"] # 0x2e34 - - "⸵": [t: "odwrócony średnik"] # 0x2e35 - - "⸶": [t: "obelisk z lewą osłoną"] # 0x2e36 - - "⸷": [t: "obelisk z prawą osłoną"] # 0x2e37 - - "⸸": [t: "odwrócony obelisk"] # 0x2e38 - - "⸹": [t: "górna połowa paragraf znak"] # 0x2e39 - - "⸺": [t: "dwa pauza"] # 0x2e3a - - "⸻": [t: "trzy pauza"] # 0x2e3b - - "〃": [t: "znak powtórzenia"] # 0x3003 - - "〈": [t: "lewy nawias kątowy"] # 0x3008 - - "〉": [t: "prawy nawias kątowy"] # 0x3009 - - "《": [t: "lewy podwójny nawias kątowy"] # 0x300a - - "》": [t: "prawy podwójny nawias kątowy"] # 0x300b - - "「": [t: "lewy nawias narożny"] # 0x300c - - "」": [t: "prawy nawias narożny"] # 0x300d - - "『": [t: "lewy biały nawias narożny"] # 0x300e - - "』": [t: "prawy biały nawias narożny"] # 0x300f - - "【": [t: "lewy czarny nawias soczewkowy"] # 0x3010 - - "】": [t: "prawy czarny nawias soczewkowy"] # 0x3011 - - "〔": [t: "lewy nawias skorupowy"] # 0x3014 - - "〕": [t: "prawy nawias skorupowy"] # 0x3015 - - "〖": [t: "lewy biały nawias soczewkowy"] # 0x3016 - - "〗": [t: "prawy biały nawias soczewkowy"] # 0x3017 - - "〘": [t: "lewy biały nawias skorupowy"] # 0x3018 - - "〙": [t: "prawy biały nawias skorupowy"] # 0x3019 - - "〚": [t: "lewy biały nawias kwadratowy"] # 0x301a - - "〛": [t: "prawy biały nawias kwadratowy"] # 0x301b - - "〜": [t: "falista kreska"] # 0x301c - - "〰": [t: "falista kreska"] # 0x3030 - - "㉈": [t: "liczba dziesięć w kółku na czarnym kwadracie"] # 0x3248 - - "㉉": [t: "liczba dwadzieścia w kółku na czarnym kwadracie"] # 0x3249 - - "㉊": [t: "liczba trzydzieści w kółku na czarnym kwadracie"] # 0x324a - - "㉋": [t: "liczba czterdzieści w kółku na czarnym kwadracie"] # 0x324b - - "㉌": [t: "liczba pięćdziesiąt w kółku na czarnym kwadracie"] # 0x324c - - "㉍": [t: "liczba sześćdziesiąt w kółku na czarnym kwadracie"] # 0x324d - - "㉎": [t: "liczba siedemdziesiąt w kółku na czarnym kwadracie"] # 0x324e - - "㉏": [t: "liczba osiemdziesiąt w kółku na czarnym kwadracie"] # 0x324f - - "㉑": [t: "w kółku numer dwadzieścia jeden"] # 0x3251 - - "㉒": [t: "w kółku numer dwadzieścia dwa"] # 0x3252 - - "㉓": [t: "w kółku numer dwadzieścia trzy"] # 0x3253 - - "㉔": [t: "w kółku numer dwadzieścia cztery"] # 0x3254 - - "㉕": [t: "w kółku numer dwadzieścia pięć"] # 0x3255 - - "㉖": [t: "w kółku numer dwadzieścia sześć"] # 0x3256 - - "㉗": [t: "w kółku numer dwadzieścia siedem"] # 0x3257 - - "㉘": [t: "w kółku numer dwadzieścia osiem"] # 0x3258 - - "㉙": [t: "w kółku numer dwadzieścia dziewięć"] # 0x3259 - - "㉚": [t: "w kółku numer trzydzieści"] # 0x325a - - "㉛": [t: "w kółku numer trzydzieści jeden"] # 0x325b - - "㉜": [t: "w kółku numer trzydzieści dwa"] # 0x325c - - "㉝": [t: "w kółku numer trzydzieści trzy"] # 0x325d - - "㉞": [t: "w kółku numer trzydzieści cztery"] # 0x325e - - "㉟": [t: "w kółku numer trzydzieści pięć"] # 0x325f - - "㊱": [t: "w kółku numer trzydzieści sześć"] # 0x32b1 - - "㊲": [t: "w kółku numer trzydzieści siedem"] # 0x32b2 (GPT-5.4 translation) - - "㊳": [t: "w kółku numer trzydzieści osiem"] # 0x32b3 (GPT-5.4 translation) - - "㊴": [t: "w kółku numer trzydzieści dziewięć"] # 0x32b4 (GPT-5.4 translation) - - "㊵": [t: "w kółku numer czterdzieści"] # 0x32b5 (GPT-5.4 translation) - - "㊶": [t: "w kółku numer czterdzieści jeden"] # 0x32b6 (GPT-5.4 translation) - - "㊷": [t: "w kółku numer czterdzieści dwa"] # 0x32b7 (GPT-5.4 translation) - - "㊸": [t: "w kółku numer czterdzieści trzy"] # 0x32b8 (GPT-5.4 translation) - - "㊹": [t: "w kółku numer czterdzieści cztery"] # 0x32b9 (GPT-5.4 translation) - - "㊺": [t: "w kółku numer czterdzieści pięć"] # 0x32ba (GPT-5.4 translation) - - "㊻": [t: "w kółku numer czterdzieści sześć"] # 0x32bb (GPT-5.4 translation) - - "㊼": [t: "w kółku numer czterdzieści siedem"] # 0x32bc (GPT-5.4 translation) - - "㊽": [t: "w kółku numer czterdzieści osiem"] # 0x32bd (GPT-5.4 translation) - - "㊾": [t: "w kółku numer czterdzieści dziewięć"] # 0x32be (GPT-5.4 translation) - - "㊿": [t: "w kółku numer pięćdziesiąt"] # 0x32bf (GPT-5.4 translation) - - "㋌": [t: "Merkury"] # 0x32cc - - "㋍": [t: "ergi"] # 0x32cd - - "㋎": [t: "elektron wolty"] # 0x32ce - - "㋏": [t: "znak ograniczonej odpowiedzialności"] # 0x32cf - - "㍱": [t: "hektopaskale"] # 0x3371 - - "㍲": [t: "daltony"] # 0x3372 - - "㍳": [t: "jednostki astronomiczne"] # 0x3373 - - "㍴": [t: "bary"] # 0x3374 + - "⸓": [T: "kropkowany obelos"] # 0x2e13 + - "⸔": [T: "dolny ancora"] # 0x2e14 + - "⸕": [T: "górny ancora"] # 0x2e15 + - "⸖": [T: "kropkowany kąt skierowany w prawo"] # 0x2e16 + - "⸗": [T: "podwójny ukośny łącznik"] # 0x2e17 + - "⸘": [T: "odwrócony interrobang"] # 0x2e18 + - "⸙": [T: "gałązka palmowa"] # 0x2e19 + - "⸚": [T: "łącznik z dierezą"] # 0x2e1a + - "⸛": [T: "tylda z pierścieniem nad"] # 0x2e1b + - "⸜": [T: "lewy dolny nawias kwadratowy parafrazy"] # 0x2e1c + - "⸝": [T: "prawy dolny nawias kwadratowy parafrazy"] # 0x2e1d + - "⸞": [T: "tylda z kropką nad"] # 0x2e1e + - "⸟": [T: "tylda z kropką pod"] # 0x2e1f + - "⸠": [T: "lewa pionowa kreska z piórem"] # 0x2e20 + - "⸡": [T: "prawa pionowa kreska z piórem"] # 0x2e21 + - "⸢": [T: "górny lewa połowa nawias kwadratowy"] # 0x2e22 + - "⸣": [T: "górny prawa połowa nawias kwadratowy"] # 0x2e23 + - "⸤": [T: "dolny lewa połowa nawias kwadratowy"] # 0x2e24 + - "⸥": [T: "dolny prawa połowa nawias kwadratowy"] # 0x2e25 + - "⸦": [T: "lewy obrócony nawias kwadratowy u"] # 0x2e26 + - "⸧": [T: "prawy obrócony nawias kwadratowy u"] # 0x2e27 + - "⸨": [T: "lewy podwójny nawias okrągły"] # 0x2e28 + - "⸩": [T: "prawy podwójny nawias okrągły"] # 0x2e29 + - "⸪": [T: "dwie kropki nad jedną kropką interpunkcyjną"] # 0x2e2a + - "⸫": [T: "jedna kropka nad dwiema kropkami interpunkcyjnymi"] # 0x2e2b + - "⸬": [T: "do kwadratu cztery kropka interpunkcyjny"] # 0x2e2c + - "⸭": [T: "pięć kropka znak"] # 0x2e2d + - "⸮": [T: "odwrócony zapytania znak"] # 0x2e2e + - "ⸯ": [T: "pionowy tylda"] # 0x2e2f + - "⸰": [T: "pierścień punkt"] # 0x2e30 + - "⸱": [T: "separator wyrazów środkowa kropka"] # 0x2e31 + - "⸲": [T: "odwrócony przecinek"] # 0x2e32 + - "⸳": [T: "podniesiony kropka"] # 0x2e33 + - "⸴": [T: "podniesiony przecinek"] # 0x2e34 + - "⸵": [T: "odwrócony średnik"] # 0x2e35 + - "⸶": [T: "obelisk z lewą osłoną"] # 0x2e36 + - "⸷": [T: "obelisk z prawą osłoną"] # 0x2e37 + - "⸸": [T: "odwrócony obelisk"] # 0x2e38 + - "⸹": [T: "górna połowa paragraf znak"] # 0x2e39 + - "⸺": [T: "dwa pauza"] # 0x2e3a + - "⸻": [T: "trzy pauza"] # 0x2e3b + - "〃": [T: "znak powtórzenia"] # 0x3003 + - "〈": [T: "lewy nawias kątowy"] # 0x3008 + - "〉": [T: "prawy nawias kątowy"] # 0x3009 + - "《": [T: "lewy podwójny nawias kątowy"] # 0x300a + - "》": [T: "prawy podwójny nawias kątowy"] # 0x300b + - "「": [T: "lewy nawias narożny"] # 0x300c + - "」": [T: "prawy nawias narożny"] # 0x300d + - "『": [T: "lewy biały nawias narożny"] # 0x300e + - "』": [T: "prawy biały nawias narożny"] # 0x300f + - "【": [T: "lewy czarny nawias soczewkowy"] # 0x3010 + - "】": [T: "prawy czarny nawias soczewkowy"] # 0x3011 + - "〔": [T: "lewy nawias skorupowy"] # 0x3014 + - "〕": [T: "prawy nawias skorupowy"] # 0x3015 + - "〖": [T: "lewy biały nawias soczewkowy"] # 0x3016 + - "〗": [T: "prawy biały nawias soczewkowy"] # 0x3017 + - "〘": [T: "lewy biały nawias skorupowy"] # 0x3018 + - "〙": [T: "prawy biały nawias skorupowy"] # 0x3019 + - "〚": [T: "lewy biały nawias kwadratowy"] # 0x301a + - "〛": [T: "prawy biały nawias kwadratowy"] # 0x301b + - "〜": [T: "falista kreska"] # 0x301c + - "〰": [T: "falista kreska"] # 0x3030 + - "㉈": [T: "liczba dziesięć w kółku na czarnym kwadracie"] # 0x3248 + - "㉉": [T: "liczba dwadzieścia w kółku na czarnym kwadracie"] # 0x3249 + - "㉊": [T: "liczba trzydzieści w kółku na czarnym kwadracie"] # 0x324a + - "㉋": [T: "liczba czterdzieści w kółku na czarnym kwadracie"] # 0x324b + - "㉌": [T: "liczba pięćdziesiąt w kółku na czarnym kwadracie"] # 0x324c + - "㉍": [T: "liczba sześćdziesiąt w kółku na czarnym kwadracie"] # 0x324d + - "㉎": [T: "liczba siedemdziesiąt w kółku na czarnym kwadracie"] # 0x324e + - "㉏": [T: "liczba osiemdziesiąt w kółku na czarnym kwadracie"] # 0x324f + - "㉑": [T: "w kółku numer dwadzieścia jeden"] # 0x3251 + - "㉒": [T: "w kółku numer dwadzieścia dwa"] # 0x3252 + - "㉓": [T: "w kółku numer dwadzieścia trzy"] # 0x3253 + - "㉔": [T: "w kółku numer dwadzieścia cztery"] # 0x3254 + - "㉕": [T: "w kółku numer dwadzieścia pięć"] # 0x3255 + - "㉖": [T: "w kółku numer dwadzieścia sześć"] # 0x3256 + - "㉗": [T: "w kółku numer dwadzieścia siedem"] # 0x3257 + - "㉘": [T: "w kółku numer dwadzieścia osiem"] # 0x3258 + - "㉙": [T: "w kółku numer dwadzieścia dziewięć"] # 0x3259 + - "㉚": [T: "w kółku numer trzydzieści"] # 0x325a + - "㉛": [T: "w kółku numer trzydzieści jeden"] # 0x325b + - "㉜": [T: "w kółku numer trzydzieści dwa"] # 0x325c + - "㉝": [T: "w kółku numer trzydzieści trzy"] # 0x325d + - "㉞": [T: "w kółku numer trzydzieści cztery"] # 0x325e + - "㉟": [T: "w kółku numer trzydzieści pięć"] # 0x325f + - "㊱": [T: "w kółku numer trzydzieści sześć"] # 0x32b1 + - "㊲": [T: "w kółku numer trzydzieści siedem"] # 0x32b2 (GPT-5.4 translation) + - "㊳": [T: "w kółku numer trzydzieści osiem"] # 0x32b3 (GPT-5.4 translation) + - "㊴": [T: "w kółku numer trzydzieści dziewięć"] # 0x32b4 (GPT-5.4 translation) + - "㊵": [T: "w kółku numer czterdzieści"] # 0x32b5 (GPT-5.4 translation) + - "㊶": [T: "w kółku numer czterdzieści jeden"] # 0x32b6 (GPT-5.4 translation) + - "㊷": [T: "w kółku numer czterdzieści dwa"] # 0x32b7 (GPT-5.4 translation) + - "㊸": [T: "w kółku numer czterdzieści trzy"] # 0x32b8 (GPT-5.4 translation) + - "㊹": [T: "w kółku numer czterdzieści cztery"] # 0x32b9 (GPT-5.4 translation) + - "㊺": [T: "w kółku numer czterdzieści pięć"] # 0x32ba (GPT-5.4 translation) + - "㊻": [T: "w kółku numer czterdzieści sześć"] # 0x32bb (GPT-5.4 translation) + - "㊼": [T: "w kółku numer czterdzieści siedem"] # 0x32bc (GPT-5.4 translation) + - "㊽": [T: "w kółku numer czterdzieści osiem"] # 0x32bd (GPT-5.4 translation) + - "㊾": [T: "w kółku numer czterdzieści dziewięć"] # 0x32be (GPT-5.4 translation) + - "㊿": [T: "w kółku numer pięćdziesiąt"] # 0x32bf (GPT-5.4 translation) + - "㋌": [T: "Merkury"] # 0x32cc + - "㋍": [T: "ergi"] # 0x32cd + - "㋎": [T: "elektron wolty"] # 0x32ce + - "㋏": [T: "znak ograniczonej odpowiedzialności"] # 0x32cf + - "㍱": [T: "hektopaskale"] # 0x3371 + - "㍲": [T: "daltony"] # 0x3372 + - "㍳": [T: "jednostki astronomiczne"] # 0x3373 + - "㍴": [T: "bary"] # 0x3374 - "㍵": [T: "o V"] # 0x3375 - - "㍶": [t: "parseki"] # 0x3376 - - "㍷": [t: "decymetry"] # 0x3377 - - "㍸": [t: "decymetry do kwadratu"] # 0x3378 - - "㍹": [t: "decymetry do sześcianu"] # 0x3379 - - "㍺": [t: "jednostki międzynarodowe"] # 0x337a - - "㎀": [t: "pikoampery"] # 0x3380 - - "㎁": [t: "nanoampery"] # 0x3381 - - "㎂": [t: "mikroampery"] # 0x3382 - - "㎃": [t: "miliampery"] # 0x3383 - - "㎄": [t: "kiloampery"] # 0x3384 - - "㎅": [t: "kilobajty"] # 0x3385 - - "㎆": [t: "megabajty"] # 0x3386 - - "㎇": [t: "gigabajty"] # 0x3387 - - "㎈": [t: "kalorie"] # 0x3388 - - "㎉": [t: "kilokalorie"] # 0x3389 - - "㎊": [t: "pikofarady"] # 0x338a - - "㎋": [t: "nanofarady"] # 0x338b - - "㎌": [t: "mikrofarady"] # 0x338c - - "㎍": [t: "mikrogramy"] # 0x338d - - "㎎": [t: "miligramy"] # 0x338e - - "㎏": [t: "kilogramy"] # 0x338f - - "㎐": [t: "herce"] # 0x3390 - - "㎑": [t: "kiloherce"] # 0x3391 - - "㎒": [t: "megaherce"] # 0x3392 - - "㎓": [t: "gigaherce"] # 0x3393 - - "㎔": [t: "teraherce"] # 0x3394 - - "㎕": [t: "mikrolitry"] # 0x3395 - - "㎖": [t: "mililitry"] # 0x3396 - - "㎗": [t: "decylitry"] # 0x3397 - - "㎘": [t: "kilolitry"] # 0x3398 - - "㎙": [t: "femtometry"] # 0x3399 - - "㎚": [t: "nanometry"] # 0x339a - - "㎛": [t: "mikrometry"] # 0x339b - - "㎜": [t: "milimetry"] # 0x339c - - "㎝": [t: "centymetry"] # 0x339d - - "㎞": [t: "kilometry"] # 0x339e - - "㎟": [t: "milimetry do kwadratu"] # 0x339f - - "㎠": [t: "centymetry do kwadratu"] # 0x33a0 - - "㎡": [t: "metry do kwadratu"] # 0x33a1 - - "㎢": [t: "kilometry do kwadratu"] # 0x33a2 - - "㎣": [t: "milimetry do sześcianu"] # 0x33a3 - - "㎤": [t: "centymetry do sześcianu"] # 0x33a4 - - "㎥": [t: "metry do sześcianu"] # 0x33a5 - - "㎦": [t: "kilometry do sześcianu"] # 0x33a6 - - "㎧": [t: "metry na drugi"] # 0x33a7 - - "㎨": [t: "metry na drugi do kwadratu"] # 0x33a8 - - "㎩": [t: "paskale"] # 0x33a9 - - "㎪": [t: "kilopaskale"] # 0x33aa - - "㎫": [t: "megapaskale"] # 0x33ab - - "㎬": [t: "gigapaskale"] # 0x33ac - - "㎭": [t: "radiany"] # 0x33ad - - "㎮": [t: "radiany na sekundę"] # 0x33ae - - "㎯": [t: "radiany na sekundę do kwadratu"] # 0x33af - - "㎰": [t: "pikosekundy"] # 0x33b0 - - "㎱": [t: "nanosekundy"] # 0x33b1 - - "㎲": [t: "mikrosekundy"] # 0x33b2 - - "㎳": [t: "milisekundy"] # 0x33b3 - - "㎴": [t: "pikowolty"] # 0x33b4 - - "㎵": [t: "nanowolty"] # 0x33b5 - - "㎶": [t: "mikrowolty"] # 0x33b6 - - "㎷": [t: "miliwolty"] # 0x33b7 - - "㎸": [t: "kilowolty"] # 0x33b8 - - "㎹": [t: "megawolty"] # 0x33b9 - - "㎺": [t: "pikowaty"] # 0x33ba - - "㎻": [t: "nanowaty"] # 0x33bb - - "㎼": [t: "mikrowaty"] # 0x33bc - - "㎽": [t: "miliwaty"] # 0x33bd - - "㎾": [t: "kilowaty"] # 0x33be - - "㎿": [t: "megawaty"] # 0x33bf - - "㏀": [t: "kilo omy"] # 0x33c0 - - "㏁": [t: "megaomy"] # 0x33c1 - - "㏂": [t: "A M"] # 0x33c2 - - "㏃": [t: "bekerele"] # 0x33c3 - - "㏄": [t: "centymetry sześcienne"] # 0x33c4 - - "㏅": [t: "kandele"] # 0x33c5 - - "㏆": [t: "kulomby na kilogram"] # 0x33c6 - - "㏇": [t: "wielkie C, o, kropka"] # 0x33c7 (I have no idea what this is) - - "㏈": [t: "decybele"] # 0x33c8 - - "㏉": [t: "greje"] # 0x33c9 - - "㏊": [t: "hektary"] # 0x33ca - - "㏋": [t: "konie mechaniczne"] # 0x33cb - - "㏌": [t: "cale"] # 0x33cc - - "㏍": [t: "kilokelwiny"] # 0x33cd - - "㏎": [t: "kilometry"] # 0x33ce - - "㏏": [t: "węzły"] # 0x33cf - - "㏐": [t: "lumeny"] # 0x33d0 + - "㍶": [T: "parseki"] # 0x3376 + - "㍷": [T: "decymetry"] # 0x3377 + - "㍸": [T: "decymetry do kwadratu"] # 0x3378 + - "㍹": [T: "decymetry do sześcianu"] # 0x3379 + - "㍺": [T: "jednostki międzynarodowe"] # 0x337a + - "㎀": [T: "pikoampery"] # 0x3380 + - "㎁": [T: "nanoampery"] # 0x3381 + - "㎂": [T: "mikroampery"] # 0x3382 + - "㎃": [T: "miliampery"] # 0x3383 + - "㎄": [T: "kiloampery"] # 0x3384 + - "㎅": [T: "kilobajty"] # 0x3385 + - "㎆": [T: "megabajty"] # 0x3386 + - "㎇": [T: "gigabajty"] # 0x3387 + - "㎈": [T: "kalorie"] # 0x3388 + - "㎉": [T: "kilokalorie"] # 0x3389 + - "㎊": [T: "pikofarady"] # 0x338a + - "㎋": [T: "nanofarady"] # 0x338b + - "㎌": [T: "mikrofarady"] # 0x338c + - "㎍": [T: "mikrogramy"] # 0x338d + - "㎎": [T: "miligramy"] # 0x338e + - "㎏": [T: "kilogramy"] # 0x338f + - "㎐": [T: "herce"] # 0x3390 + - "㎑": [T: "kiloherce"] # 0x3391 + - "㎒": [T: "megaherce"] # 0x3392 + - "㎓": [T: "gigaherce"] # 0x3393 + - "㎔": [T: "teraherce"] # 0x3394 + - "㎕": [T: "mikrolitry"] # 0x3395 + - "㎖": [T: "mililitry"] # 0x3396 + - "㎗": [T: "decylitry"] # 0x3397 + - "㎘": [T: "kilolitry"] # 0x3398 + - "㎙": [T: "femtometry"] # 0x3399 + - "㎚": [T: "nanometry"] # 0x339a + - "㎛": [T: "mikrometry"] # 0x339b + - "㎜": [T: "milimetry"] # 0x339c + - "㎝": [T: "centymetry"] # 0x339d + - "㎞": [T: "kilometry"] # 0x339e + - "㎟": [T: "milimetry do kwadratu"] # 0x339f + - "㎠": [T: "centymetry do kwadratu"] # 0x33a0 + - "㎡": [T: "metry do kwadratu"] # 0x33a1 + - "㎢": [T: "kilometry do kwadratu"] # 0x33a2 + - "㎣": [T: "milimetry do sześcianu"] # 0x33a3 + - "㎤": [T: "centymetry do sześcianu"] # 0x33a4 + - "㎥": [T: "metry do sześcianu"] # 0x33a5 + - "㎦": [T: "kilometry do sześcianu"] # 0x33a6 + - "㎧": [T: "metry na drugi"] # 0x33a7 + - "㎨": [T: "metry na drugi do kwadratu"] # 0x33a8 + - "㎩": [T: "paskale"] # 0x33a9 + - "㎪": [T: "kilopaskale"] # 0x33aa + - "㎫": [T: "megapaskale"] # 0x33ab + - "㎬": [T: "gigapaskale"] # 0x33ac + - "㎭": [T: "radiany"] # 0x33ad + - "㎮": [T: "radiany na sekundę"] # 0x33ae + - "㎯": [T: "radiany na sekundę do kwadratu"] # 0x33af + - "㎰": [T: "pikosekundy"] # 0x33b0 + - "㎱": [T: "nanosekundy"] # 0x33b1 + - "㎲": [T: "mikrosekundy"] # 0x33b2 + - "㎳": [T: "milisekundy"] # 0x33b3 + - "㎴": [T: "pikowolty"] # 0x33b4 + - "㎵": [T: "nanowolty"] # 0x33b5 + - "㎶": [T: "mikrowolty"] # 0x33b6 + - "㎷": [T: "miliwolty"] # 0x33b7 + - "㎸": [T: "kilowolty"] # 0x33b8 + - "㎹": [T: "megawolty"] # 0x33b9 + - "㎺": [T: "pikowaty"] # 0x33ba + - "㎻": [T: "nanowaty"] # 0x33bb + - "㎼": [T: "mikrowaty"] # 0x33bc + - "㎽": [T: "miliwaty"] # 0x33bd + - "㎾": [T: "kilowaty"] # 0x33be + - "㎿": [T: "megawaty"] # 0x33bf + - "㏀": [T: "kilo omy"] # 0x33c0 + - "㏁": [T: "megaomy"] # 0x33c1 + - "㏂": [T: "A M"] # 0x33c2 + - "㏃": [T: "bekerele"] # 0x33c3 + - "㏄": [T: "centymetry sześcienne"] # 0x33c4 + - "㏅": [T: "kandele"] # 0x33c5 + - "㏆": [T: "kulomby na kilogram"] # 0x33c6 + - "㏇": [T: "wielkie C, o, kropka"] # 0x33c7 (I have no idea what this is) + - "㏈": [T: "decybele"] # 0x33c8 + - "㏉": [T: "greje"] # 0x33c9 + - "㏊": [T: "hektary"] # 0x33ca + - "㏋": [T: "konie mechaniczne"] # 0x33cb + - "㏌": [T: "cale"] # 0x33cc + - "㏍": [T: "kilokelwiny"] # 0x33cd + - "㏎": [T: "kilometry"] # 0x33ce + - "㏏": [T: "węzły"] # 0x33cf + - "㏐": [T: "lumeny"] # 0x33d0 - "㏑": [T: "logarytm naturalny"] # 0x33d1 - - "㏒": [t: "logarytm"] # 0x33d2 - - "㏓": [t: "luksy"] # 0x33d3 - - "㏔": [t: "milibarny"] # 0x33d4 - - "㏕": [t: "mile"] # 0x33d5 - - "㏖": [t: "mole"] # 0x33d6 + - "㏒": [T: "logarytm"] # 0x33d2 + - "㏓": [T: "luksy"] # 0x33d3 + - "㏔": [T: "milibarny"] # 0x33d4 + - "㏕": [T: "mile"] # 0x33d5 + - "㏖": [T: "mole"] # 0x33d6 - "㏗": [T: "p h"] # 0x33d7 - - "㏘": [t: "pikometry"] # 0x33d8 - - "㏙": [t: "części na milion"] # 0x33d9 - - "㏚": [t: "petarentgeny"] # 0x33da - - "㏛": [t: "steradiany"] # 0x33db - - "㏜": [t: "siwerty"] # 0x33dc - - "㏝": [t: "webery"] # 0x33dd - - "㏞": [t: "wolty na metr"] # 0x33de - - "㏟": [t: "ampery na metr"] # 0x33df - - "㏿": [t: "galony"] # 0x33ff - - "": [t: "równa się z daszkiem pod"] # 0xe900 - - "": [t: "równa się z plusem nad"] # 0xe901 - - "": [t: "równa się z plusem pod"] # 0xe902 - - "": [t: "tylda z plusem nad"] # 0xe903 - - "": [t: "tylda z plusem pod"] # 0xe904 - - "": [t: "równe podwójny przez większe niż"] # 0xe908 - - "": [t: "równe podwójny przez mniejsze niż"] # 0xe909 - - "": [t: "zawiera lub równe"] # 0xe90a - - "": [t: "nadzbiór lub równy"] # 0xe90b - - "": [t: "podzbiór lub równy"] # 0xe90c - - "": [t: "równe przez mniejsze niż"] # 0xe90d - - "": [t: "należy do lub równe"] # 0xe912 - - "": [t: "równe lub większe niż"] # 0xe913 - - "": [t: "przybliżone nadzbiór"] # 0xe914 - - "": [t: "przybliżone podzbiór"] # 0xe915 - - "": [t: "nadzbiór z kropką zawiera jako relację indeksu dolnego"] # 0xe916 - - "": [t: "podzbiór z kropką jest zawarty jako relacja indeksu dolnego"] # 0xe917 - - "": [t: "równe z kropką pod"] # 0xe918 - - "": [t: "lewy kropka nad minus przez prawy kropka"] # 0xe919 - - "": [t: "prawy kropka nad minus przez lewy kropka"] # 0xe91a - - "": [t: "w przybliżeniu równe minus"] # 0xe91f - - "": [t: "podwójna kwadratowa suma zbiorów"] # 0xe920 - - "": [t: "podwójny kwadrat wielka"] # 0xe921 - - "": [t: "mniejsze niż równe lub większe niż"] # 0xe922 - - "": [t: "tylda z kropką"] # 0xe924 - - "": [t: "tylda z dwiema kropkami"] # 0xe925 - - "": [t: "mniejsze niż większe lub równe"] # 0xe926 - - "": [t: "większe niż mniejsze lub równe"] # 0xe927 - - "": [t: "Równoważne lub mniejsze niż"] # 0xe928 - - "": [t: "Równoważne lub większe niż"] # 0xe929 - - "": [t: "lewy otwórz ramka operator"] # 0xe92a - - "": [t: "prawy otwórz ramka operator"] # 0xe92b - - "": [t: "identyczne z kropką"] # 0xe92c - - "": [t: "większe niż równe lub mniejsze niż"] # 0xe92d - - "": [t: "kreska operator"] # 0xe92e - - "": [t: "podwójny kreska operator"] # 0xe92f - - "": [t: "potrójny kreska operator"] # 0xe930 - - "": [t: "mniejsze niż lub w przybliżeniu równe"] # 0xe932 - - "": [t: "większe niż lub w przybliżeniu równe"] # 0xe933 - - "": [t: "zagnieżdżone mniejsze niż"] # 0xe936 - - "": [t: "zagnieżdżone większe niż"] # 0xe937 - - "": [t: "poprzedza lub Równoważne"] # 0xe93a - - "": [t: "następuje po lub Równoważne"] # 0xe93b - - "": [t: "poprzedza nad równe"] # 0xe940 - - "": [t: "następuje po nad równe"] # 0xe941 - - "": [t: "mniejsze niż nad ukośnie równe nad większe niż"] # 0xe942 - - "": [t: "większe niż nad ukośnie równe nad mniejsze niż"] # 0xe943 - - "": [t: "spełnione przez"] # 0xe948 - - "": [t: "leniwe s"] # 0xe949 - - "": [t: "nie asercja"] # 0xe94a - - "": [t: "podwójny równe"] # 0xe94b - - "": [t: "potrójny równe"] # 0xe94c - - "": [t: "reguła opóźniona"] # 0xe94d - - "": [t: "alias ogranicznika"] # 0xe94e - - "": [t: "normalna podgrupa z kreską"] # 0xe950 - - "": [t: "zawiera jako normalny podgrupa z kreską"] # 0xe951 - - "": [t: "zaokrąglone implikuje"] # 0xe954 - - "": [t: "uśmiech pod kreską"] # 0xe955 - - "": [t: "smutek przez kreskę"] # 0xe956 - - "": [t: "nadzbiór lub w przybliżeniu równe"] # 0xe957 - - "": [t: "podzbiór lub w przybliżeniu równe"] # 0xe958 - - "": [t: "większe niż w przybliżeniu równe lub mniejsze niż"] # 0xe959 - - "": [t: "mniejsze niż w przybliżeniu równe lub większe niż"] # 0xe95a - - "": [t: "podwójny logiczne lub"] # 0xe95c - - "": [t: "podwójna koniunkcja logiczna"] # 0xe95d - - "": [t: "alternatywa logiczna z podwójną kreską pod"] # 0xe95e - - "": [t: "logiczne lub z kreską pod"] # 0xe95f - - "": [t: "w przybliżeniu równe nad równe"] # 0xe962 - - "": [t: "trójkąt skierowany w lewo z kreską dzielącą"] # 0xe964 - - "": [t: "trójkąt skierowany w prawo z kreską dzielącą"] # 0xe965 - - "": [t: "równa się z kropkowaną górną linią"] # 0xe966 - - "": [t: "poprzedza z dwukropkiem"] # 0xe967 - - "": [t: "następuje po z dwukropkiem"] # 0xe968 - - "": [t: "mniejsze niż lub ukośnie równe"] # 0xe969 - - "": [t: "większe niż lub ukośnie równe"] # 0xe96a - - "": [t: "zagnieżdżone bardzo znacznie mniejsze niż"] # 0xe96b - - "": [t: "zagnieżdżone bardzo znacznie większe niż"] # 0xe96c - - "": [t: "wariant różnicy między"] # 0xe96d - - "": [t: "mniejsze niż większe niż nakładka"] # 0xe96e - - "": [t: "alternatywa logiczna z nałożoną koniunkcją logiczną"] # 0xe96f - - "": [t: "nadzbiór przez nadzbiór"] # 0xe970 - - "": [t: "podzbiór przez podzbiór"] # 0xe971 - - "": [t: "nadzbiór przez podzbiór"] # 0xe972 - - "": [t: "podzbiór przez nadzbiór"] # 0xe973 - - "": [t: "potrójna pionowa kreska"] # 0xe979 - - "": [t: "sparowane poczwórne pionowe kropki"] # 0xe97a - - "": [t: "prostopadłe przez kreskę"] # 0xe97b - - "": [t: "lewy znak dedukcji z podwójną pionową kreską"] # 0xe97c - - "": [t: "podwójny lewy znak dedukcji z podwójną pionową kreską"] # 0xe97d - - "": [t: "prostopadłe przez odwrócony prostopadłe"] # 0xe97e - - "": [t: "podwójny lewy znak dedukcji z pionową kreską"] # 0xe97f - - "": [t: "kąt sferyczny otwarty w górę"] # 0xe980 - - "": [t: "podwójny ukośnik"] # 0xe981 - - "": [t: "kąt prosty z narożnikiem"] # 0xe982 - - "": [t: "pionowa kreska w kółku"] # 0xe984 - - "": [t: "znak dzielenia w kółku"] # 0xe985 - - "": [t: "kreskowany ukośnik"] # 0xe986 - - "": [t: "kreskowany ukośnik odwrotny"] # 0xe987 - - "": [t: "kreskowana linia środkowa"] # 0xe988 - - "": [t: "kreskowana pionowa kreska"] # 0xe989 - - "": [t: "prostopadłe z s"] # 0xe98a - - "": [t: "kąt z s"] # 0xe98b - - "": [t: "kąt sferyczny otwarty w lewo"] # 0xe98c - - "": [t: "kąt otwarty w lewo"] # 0xe98d - - "": [t: "pionowa kreska z podwójnym haczykiem"] # 0xe98e - - "": [t: "operator średniej kropki wolnego rodnika"] # 0xe98f - - "": [t: "biały trójkąt skierowany w górę nad kreską"] # 0xe990 - - "": [t: "identyczne i równoległe do"] # 0xe991 - - "": [t: "iloczyn spłaszczony"] # 0xe992 - - "": [t: "potrójny operator kreski z kreską poziomą"] # 0xe993 - - "": [t: "identyczne z podwójnym ukośnikiem"] # 0xe994 - - "": [t: "potrójne skrzyżowane kreski"] # 0xe995 - - "": [t: "pionowa kreska przez okrąg"] # 0xe996 - - "": [t: "pionowy proporcjonalne do"] # 0xe997 - - "": [t: "czarna ostatnia kwadra księżyca"] # 0xe998 - - "": [t: "czarna pierwsza kwadra księżyca"] # 0xe999 - - "": [t: "ujemna fala sinusoidalna"] # 0xe9a0 - - "": [t: "kropka w nawiasach"] # 0xe9a1 - - "": [t: "nawiasy"] # 0xe9a2 - - "": [t: "biały uśmiech"] # 0xe9a3 - - "": [t: "biały smutek"] # 0xe9a4 - - "": [t: "sześciokąt"] # 0xe9a5 - - "": [t: "Równoważne nad plus"] # 0xe9a6 - - "": [t: "plus przez Równoważne"] # 0xe9a7 - - "": [t: "część wspólna z szeryfami"] # 0xe9b0 - - "": [t: "suma zbiorów z szeryfami"] # 0xe9b1 - - "": [t: "kwadratowa część wspólna z szeryfami"] # 0xe9b2 - - "": [t: "kwadratowa suma zbiorów z szeryfami"] # 0xe9b3 - - "": [t: "poprzedza Równoważne lub następuje po"] # 0xe9e0 - - "": [t: "następuje po Równoważne lub poprzedza"] # 0xe9e1 - - "": [t: "poprzedza w przybliżeniu równe lub następuje po"] # 0xe9e2 - - "": [t: "następuje po w przybliżeniu równe lub poprzedza"] # 0xe9e3 - - "": [t: "mniejsze niż Równoważne lub większe niż"] # 0xe9f0 - - "": [t: "większe niż Równoważne lub mniejsze niż"] # 0xe9f1 - - "": [t: "pionowo przekreślone znacznie mniejsze niż"] # 0xea00 - - "": [t: "pionowo przekreślone znacznie większe niż"] # 0xea01 - - "": [t: "nie jest znacznie mniejsze niż, wariant"] # 0xea02 - - "": [t: "nie jest znacznie większe niż, wariant"] # 0xea03 - - "": [t: "mniejsze niż z pionową kreską i nie podwójne równe"] # 0xea04 - - "": [t: "większe niż z pionową kreską i nie podwójne równe"] # 0xea05 - - "": [t: "nie mniejsze lub równe"] # 0xea06 - - "": [t: "nie większe lub równe"] # 0xea07 - - "": [t: "ani równe, ani mniejsze niż"] # 0xea09 - - "": [t: "nie zawiera lub równe"] # 0xea0a - - "": [t: "ani nadzbiór, ani równe"] # 0xea0b - - "": [t: "ani podzbiór, ani równe"] # 0xea0c - - "": [t: "ukośnik odwrotny z podzbiorem"] # 0xea0d - - "": [t: "ani równe, ani większe niż"] # 0xea0e - - "": [t: "nie minus operator tyldy"] # 0xea0f - - "": [t: "ani równe, ani mniejsze niż"] # 0xea10 - - "": [t: "nie operator tyldy"] # 0xea11 - - "": [t: "nie jest elementem zbioru lub równe"] # 0xea12 - - "": [t: "ani równe, ani większe niż"] # 0xea13 - - "": [t: "nie w przybliżeniu równe"] # 0xea14 - - "": [t: "nie następuje po podobne"] # 0xea15 - - "": [t: "mniejsze niż lub ukośnie równe z ukośnikiem"] # 0xea16 - - "": [t: "większe niż lub ukośnie równe z ukośnikiem"] # 0xea17 - - "": [t: "nadzbiór ukośnik"] # 0xea1a - - "": [t: "nie zawiera"] # 0xea1b - - "": [t: "nie mniejsze lub równe"] # 0xea1d - - "": [t: "nie większe lub równe"] # 0xea1e - - "": [t: "nie w przybliżeniu równe minus"] # 0xea1f - - "": [t: "zanegowana przynależność do zbioru z kropką nad"] # 0xea22 - - "": [t: "nie kąt pionowy"] # 0xea2c - - "": [t: "nie ukośnie równoległe"] # 0xea2d - - "": [t: "nie kreska operator"] # 0xea2e - - "": [t: "nie podwójny kreska operator"] # 0xea2f - - "": [t: "nie potrójny kreska operator"] # 0xea30 - - "": [t: "mniejsze niż ale nie w przybliżeniu równe"] # 0xea32 - - "": [t: "większe niż ale nie w przybliżeniu równe"] # 0xea33 - - "": [t: "mniejsze niż lub nie równa się"] # 0xea34 - - "": [t: "większe niż lub nie równa się"] # 0xea35 - - "": [t: "nie zagnieżdżone mniejsze niż"] # 0xea36 - - "": [t: "nie zagnieżdżone większe niż"] # 0xea37 - - "": [t: "nie jest znacznie mniejsze niż"] # 0xea38 - - "": [t: "nie jest znacznie większe niż"] # 0xea39 - - "": [t: "poprzedza ale nie Równoważne"] # 0xea3a - - "": [t: "następuje po ale nie Równoważne"] # 0xea3b - - "": [t: "poprzedza ale nie równa się"] # 0xea3c - - "": [t: "następuje po ale nie równa się"] # 0xea3d - - "": [t: "nie równa się ani nie poprzedza"] # 0xea3e - - "": [t: "nie równa się ani nie następuje po"] # 0xea3f - - "": [t: "poprzedza ale nie równa się"] # 0xea40 - - "": [t: "następuje po ale nie równa się"] # 0xea41 - - "": [t: "ani podzbiór, ani równe"] # 0xea42 - - "": [t: "ani nadzbiór, ani równe"] # 0xea43 - - "": [t: "podzbiór lub nie równa się"] # 0xea44 - - "": [t: "nadzbiór lub nie równa się"] # 0xea45 - - "": [t: "ani podzbiór, ani równe"] # 0xea46 - - "": [t: "ani nadzbiór, ani równe"] # 0xea47 - - "": [t: "nie potrójny mniejsze niż"] # 0xea48 - - "": [t: "nie potrójny większe niż"] # 0xea49 - - "": [t: "nie poprzedza równa się"] # 0xea4c - - "": [t: "nie następuje po równa się"] # 0xea4d - - "": [t: "nie normalna podgrupa z kreską"] # 0xea50 - - "": [t: "nie zawiera jako normalnej podgrupy z kreską"] # 0xea51 - - "": [t: "nie różnica między"] # 0xea52 - - "": [t: "nie geometrycznie równoważne"] # 0xea53 - - "": [t: "nie pionowo podobne"] # 0xea54 - - "": [t: "nie równe lub podobne"] # 0xea55 - - "": [t: "nie pionowo przybliżone"] # 0xea56 - - "": [t: "nie w przybliżeniu identyczne z"] # 0xea57 - - "": [t: "nie wyboiście równe"] # 0xea58 - - "": [t: "nie wyboiście pojedynczo równe"] # 0xea59 - - "": [t: "nie równe kropka"] # 0xea5a - - "": [t: "odwrotne nie równoważne"] # 0xea5b - - "": [t: "nie kwadrat podzbiór"] # 0xea60 - - "": [t: "nie kwadrat nadzbiór"] # 0xea61 - - "": [t: "nie w przybliżeniu równe nad znakiem równości"] # 0xea62 - - "": [t: "nie ściśle równoważne"] # 0xea63 - - "": [t: "nie przystające z kropką"] # 0xea64 - - "": [t: "odwrotnie nie równe"] # 0xea65 - - "": [t: "nie pionowy lewy trójkąt równa się"] # 0xea70 - - "": [t: "nie pionowy prawy trójkąt równa się"] # 0xea71 - - "": [t: "nie cząstkowe"] # 0xea80 - - "": [t: "ozdobnik przedłużenia strzałki"] # 0xeb00 - - "": [t: "strzałka prawy przeze strzałką lewy"] # 0xeb01 - - "": [t: "strzałka prawy przeze strzałką lewy"] # 0xeb02 - - "": [t: "harpun prawy przez harpun lewy"] # 0xeb03 - - "": [t: "harpun prawy przez harpun lewy"] # 0xeb04 - - "": [t: "podwójna strzałka północny wschód południowy zachód"] # 0xeb05 - - "": [t: "podwójna strzałka północny zachód południowy wschód"] # 0xeb06 - - "": [t: "poziome przedłużenie harpuna"] # 0xeb07 - - "": [t: "łuk ze strzałką w lewo przeciwnie do ruchu wskazówek zegara"] # 0xeb08 - - "": [t: "łuk ze strzałką w prawo przeciwnie do ruchu wskazówek zegara"] # 0xeb09 - - "": [t: "duży strzałka w prawo akcent"] # 0xeb0b - - "": [t: "duży strzałka w lewo akcent"] # 0xeb0c - - "": [t: "lewy grot strzałki"] # 0xeb0d - - "": [t: "prawy grot strzałki"] # 0xeb0e - - "": [t: "duża strzałka w prawo z lewym przekreśleniem"] # 0xeb0f - - "": [t: "poziome przedłużenie podwójnej strzałki"] # 0xeb10 - - "": [t: "duża podwójna strzałka w lewo i prawo z przekreśleniem"] # 0xeb11 - - "": [t: "strzałka w dół po lewej i strzałka w górę"] # 0xeb12 - - "": [t: "strzałka w lewo z dolnym narożnikiem"] # 0xeb13 - - "": [t: "strzałka w prawo z górnym narożnikiem"] # 0xeb14 - - "": [t: "strzałka w lewo z górnym narożnikiem"] # 0xeb15 - - "": [t: "strzałka po górnym półokręgu przeciwnie do ruchu wskazówek zegara z plusem"] # 0xeb16 - - "": [t: "strzałka po górnym półokręgu zgodnie z ruchem wskazówek zegara z minusem"] # 0xeb17 - - "": [t: "strzałka w prawo z ogonem i przekreśleniem"] # 0xeb18 - - "": [t: "prawy harpun dolny"] # 0xeb19 - - "": [t: "lewy harpun dolny"] # 0xeb1a - - "": [t: "lewy prawy harpun dolny"] # 0xeb1b - - "": [t: "lewy prawy harpun górny"] # 0xeb1c - - "": [t: "górny dolny harpun lewy"] # 0xeb1d - - "": [t: "górny dolny harpun prawy"] # 0xeb1e - - "": [t: "strzałka w górę w prawo ze strzałką w dół"] # 0xeb1f - - "": [t: "lewy harpun do kreski z grotem górnym"] # 0xeb20 - - "": [t: "prawy harpun do kreski z grotem górnym"] # 0xeb21 - - "": [t: "lewy harpun do kreski z grotem dolnym"] # 0xeb22 - - "": [t: "prawy harpun do kreski z grotem dolnym"] # 0xeb23 - - "": [t: "lewy harpun od kreski z grotem górnym"] # 0xeb24 - - "": [t: "prawy harpun od kreski z grotem górnym"] # 0xeb25 - - "": [t: "lewy harpun od kreski z grotem dolnym"] # 0xeb26 - - "": [t: "prawy harpun od kreski z grotem dolnym"] # 0xeb27 - - "": [t: "górny harpun do kreski z grotem lewym"] # 0xeb28 - - "": [t: "dolny harpun do kreski z grotem lewym"] # 0xeb29 - - "": [t: "górny harpun do kreski z grotem prawym"] # 0xeb2a - - "": [t: "dolny harpun do kreski z grotem prawym"] # 0xeb2b - - "": [t: "górny harpun od kreski z grotem lewym"] # 0xeb2c - - "": [t: "dolny harpun od kreski z grotem lewym"] # 0xeb2d - - "": [t: "górny harpun od kreski z grotem prawym"] # 0xeb2e - - "": [t: "dolny harpun od kreski z grotem prawym"] # 0xeb2f - - "": [t: "strzałka w górę do kreski"] # 0xeb30 - - "": [t: "strzałka w dół do kreski"] # 0xeb31 - - "": [t: "górny harpun w lewo z dolnym harpunem"] # 0xeb32 - - "": [t: "górny harpun w prawo z dolnym harpunem"] # 0xeb33 - - "": [t: "górny grot strzałki"] # 0xeb34 - - "": [t: "dolny grot strzałki"] # 0xeb35 - - "": [t: "podwójny harpun z lewym grotem dolnym i prawym grotem górnym"] # 0xeb36 - - "": [t: "podwójny harpun z lewym grotem górnym i prawym grotem dolnym"] # 0xeb37 - - "": [t: "strzałka w lewo przez kreskę"] # 0xeb38 - - "": [t: "strzałka w prawo przez kreskę"] # 0xeb39 - - "": [t: "strzałka w lewo pod kreski"] # 0xeb3a - - "": [t: "strzałka w prawo pod kreski"] # 0xeb3b - - "": [t: "lewy prawy potrójny strzałka"] # 0xeb3c - - "": [t: "podwójna strzałka północny wschód południowy wschód"] # 0xeb3f - - "": [t: "strzałka po lewym półokręgu przeciwnie do ruchu wskazówek zegara"] # 0xeb40 - - "": [t: "strzałka po lewym półokręgu zgodnie z ruchem wskazówek zegara"] # 0xeb41 - - "": [t: "lewy otwarty okrąg lewy strzałka w prawo"] # 0xeb42 - - "": [t: "strzałka w prawo przez tylda"] # 0xeb44 - - "": [t: "strzałka w lewo przez tylda"] # 0xeb45 - - "": [t: "lewy harpun przez kreskę"] # 0xeb48 - - "": [t: "prawy harpun przez kreskę"] # 0xeb49 - - "": [t: "lewy harpun pod kreski"] # 0xeb4a - - "": [t: "prawy harpun pod kreski"] # 0xeb4b - - "": [t: "krótka czarna strzałka w lewo"] # 0xeb4c - - "": [t: "strzałka po prawym półokręgu zgodnie z ruchem wskazówek zegara"] # 0xeb50 - - "": [t: "strzałka po prawym półokręgu przeciwnie do ruchu wskazówek zegara"] # 0xeb51 - - "": [t: "lewy otwarty okrąg lewy prawy harpun"] # 0xeb52 - - "": [t: "strzałka w górę lewy z pionową kreską"] # 0xeb58 - - "": [t: "strzałka w dół lewy z pionową kreską"] # 0xeb59 - - "": [t: "strzałka w górę prawy z pionową kreską"] # 0xeb5a - - "": [t: "strzałka w dół prawy z pionową kreską"] # 0xeb5b - - "": [t: "strzałka w prawo z wydłużonym dolnym hakiem"] # 0xeb5c - - "": [t: "strzałka w lewo z wydłużonym hakiem"] # 0xeb5d - - "": [t: "strzałka w lewo z wydłużonym dolnym hakiem"] # 0xeb5e - - "": [t: "strzałka w prawo z wydłużonym hakiem"] # 0xeb5f - - "": [t: "nie strzałka w prawo falista"] # 0xeb60 - - "": [t: "nie strzałka w prawo zakrzywiony"] # 0xeb61 - - "": [t: "górny harpun lewy z pionową kreską"] # 0xeb68 - - "": [t: "dolny harpun lewy z pionową kreską"] # 0xeb69 - - "": [t: "górny harpun prawy z pionową kreską"] # 0xeb6a - - "": [t: "dolny harpun prawy z pionową kreską"] # 0xeb6b - - "": [t: "pionowe przedłużenie podwójnej strzałki"] # 0xeb6c - - "": [t: "pionowe przedłużenie harpuna z grotem w lewo"] # 0xeb6d - - "": [t: "pionowe przedłużenie harpuna z grotem w prawo"] # 0xeb6e - - "": [t: "prawy harpun przez lewy harpun prawy"] # 0xeb6f - - "": [t: "prawy harpun przez lewy harpun lewy"] # 0xeb70 - - "": [t: "lewy harpun przez prawy harpun prawy"] # 0xeb71 - - "": [t: "lewy harpun przez prawy harpun lewy"] # 0xeb72 - - "": [t: "strzałka w lewo od kreski z grotem strzałki"] # 0xeb73 - - "": [t: "przedłużenie strzałki w prawo od lewej kreski"] # 0xeb74 - - "": [t: "strzałka w lewo od kreski ogon"] # 0xeb75 - - "": [t: "strzałka w prawo od kreski ogon"] # 0xeb76 - - "": [t: "strzałka w prawo od kreski z grotem strzałki"] # 0xeb77 - - "": [t: "górny harpun od kreski z lewym grotem strzałki"] # 0xeb78 - - "": [t: "strzałka w prawo nad strzałką w lewo prawy"] # 0xeb79 - - "": [t: "strzałka w prawo nad strzałką w lewo lewy"] # 0xeb7a - - "": [t: "strzałka w lewo nad strzałką w prawo prawy"] # 0xeb7b - - "": [t: "strzałka w lewo nad strzałką w prawo lewy"] # 0xeb7c - - "": [t: "strzałka w górę od kreski z grotem strzałki"] # 0xeb7d - - "": [t: "strzałka w górę od kreski ogon"] # 0xeb7e - - "": [t: "strzałka w dół od kreski ogon"] # 0xeb7f - - "": [t: "strzałka w dół od kreski z grotem strzałki"] # 0xeb80 - - "": [t: "dolny harpun od kreski z prawym grotem strzałki"] # 0xeb81 - - "": [t: "górny harpun w lewo z dolnym harpunem dolny"] # 0xeb82 - - "": [t: "przedłużenie górnego harpuna w lewo i dolnego harpuna"] # 0xeb83 - - "": [t: "dolny harpun w lewo z górnym harpunem górny"] # 0xeb84 - - "": [t: "górny harpun w lewo z dolnym harpunem górny"] # 0xeb85 - - "": [t: "przedłużenie dolnego harpuna w lewo i górnego harpuna"] # 0xeb86 - - "": [t: "dolny harpun w lewo z górnym harpunem dolny"] # 0xeb87 - - "": [t: "strzałka w górę po lewej i strzałka w dół dolny"] # 0xeb88 - - "": [t: "strzałka w dół po lewej i strzałka w górę górny"] # 0xeb89 - - "": [t: "strzałka w górę po lewej i strzałka w dół górny"] # 0xeb8a - - "": [t: "strzałka w dół po lewej i strzałka w górę dolny"] # 0xeb8b - - "": [t: "przedłużenie strzałki w lewo i w prawo"] # 0xeb8c - - "": [t: "przedłużenie strzałki północno-wschodniej"] # 0xeb8d - - "": [t: "przedłużenie strzałki północno-zachodniej"] # 0xeb8e - - "": [t: "lewa część dolnego nawiasu klamrowego"] # 0xec00 - - "": [t: "środkowa część dolnego nawiasu klamrowego"] # 0xec01 - - "": [t: "prawa część dolnego nawiasu klamrowego"] # 0xec02 - - "": [t: "poziome przedłużenie nawiasu klamrowego"] # 0xec03 - - "": [t: "lewa część górnego nawiasu klamrowego"] # 0xec04 - - "": [t: "środkowa część górnego nawiasu klamrowego"] # 0xec05 - - "": [t: "prawa część górnego nawiasu klamrowego"] # 0xec06 - - "": [t: "lewa pionowa kreska"] # 0xec07 - - "": [t: "prawa pionowa kreska"] # 0xec08 - - "": [t: "lewa podwójna pionowa kreska"] # 0xec09 - - "": [t: "prawa podwójna pionowa kreska"] # 0xec0a - - "": [t: "poziome przedłużenie nawiasu kwadratowego"] # 0xec0b - - "": [t: "dolny nawias kwadratowy"] # 0xec0c - - "⎵": [t: "dolny nawias kwadratowy"] # 0x23b5 - - "": [t: "górny nawias kwadratowy"] # 0xec0d - - "⎴": [t: "górny nawias kwadratowy"] # 0x23b4 - - "": [t: "pod nawias kwadratowy lewy"] # 0xec0e - - "": [t: "pod nawias kwadratowy prawy"] # 0xec0f - - "": [t: "przez nawias kwadratowy lewy"] # 0xec10 - - "": [t: "przez nawias kwadratowy prawy"] # 0xec11 - - "": [t: "lewy nawias okrągły 1"] # 0xec12 - - "": [t: "lewy nawias okrągły 2"] # 0xec13 - - "": [t: "lewy nawias okrągły 3"] # 0xec14 - - "": [t: "lewy nawias okrągły 4"] # 0xec15 - - "": [t: "prawy nawias okrągły 1"] # 0xec16 - - "": [t: "prawy nawias okrągły 2"] # 0xec17 - - "": [t: "prawy nawias okrągły 3"] # 0xec18 - - "": [t: "prawy nawias okrągły 4"] # 0xec19 - - "": [t: "znak pierwiastka 1"] # 0xec1a - - "": [t: "znak pierwiastka 2"] # 0xec1b - - "": [t: "znak pierwiastka 3"] # 0xec1c - - "": [t: "znak pierwiastka 4"] # 0xec1d - - "": [t: "znak pierwiastka 5"] # 0xec1e - - "": [t: "dolna część znaku pierwiastka"] # 0xec1f - - "": [t: "pionowe przedłużenie znaku pierwiastka"] # 0xec20 - - "": [t: "górna część znaku pierwiastka"] # 0xec21 - - "": [t: "lewy biały nawias kwadratowy górny"] # 0xec22 - - "": [t: "przedłużenie lewego białego nawiasu kwadratowego"] # 0xec23 - - "": [t: "lewy biały nawias kwadratowy dolny"] # 0xec24 - - "": [t: "prawy biały nawias kwadratowy górny"] # 0xec25 - - "": [t: "przedłużenie prawego białego nawiasu kwadratowego"] # 0xec26 - - "": [t: "prawy biały nawias kwadratowy dolny"] # 0xec27 - - "": [t: "lewy biały zakrzywiony nawias kwadratowy"] # 0xec30 - - "": [t: "prawy biały zakrzywiony nawias kwadratowy"] # 0xec31 - - "": [t: "znak dzielenia pisemnego"] # 0xec32 - - "": [t: "przedłużenie znaku dzielenia pisemnego"] # 0xec33 - - "": [t: "krótkie dzielenie"] # 0xec34 - - "": [t: "podwójne wiązanie em od południowego zachodu do północnego wschodu"] # 0xec40 - - "": [t: "podwójne wiązanie em od północnego zachodu do południowego wschodu"] # 0xec41 - - "": [t: "pojedyncze poziome wiązanie em"] # 0xec42 - - "": [t: "podwójne poziome wiązanie em"] # 0xec43 - - "": [t: "potrójne poziome wiązanie em"] # 0xec44 - - "": [t: "pojedyncze pionowe wiązanie em"] # 0xec45 - - "": [t: "podwójne pionowe wiązanie em"] # 0xec46 - - "": [t: "potrójne pionowe wiązanie em"] # 0xec47 - - "": [t: "wiązanie em mniejsze niż"] # 0xec48 - - "": [t: "wiązanie em większe niż"] # 0xec49 - - "": [t: "pojedyncze poziome wiązanie en"] # 0xec4a - - "": [t: "podwójne poziome wiązanie en"] # 0xec4b - - "": [t: "potrójne poziome wiązanie en"] # 0xec4c - - "": [t: "górny lewy prostokąt"] # 0xec80 - - "": [t: "dolny lewy prostokąt"] # 0xec81 - - "": [t: "górny prawy prostokąt"] # 0xec90 - - "": [t: "dolny prawy prostokąt"] # 0xec91 - - "": [t: "narożnik dzielenia syntetycznego"] # 0xec92 - - "": [t: "poziome przedłużenie dzielenia syntetycznego"] # 0xec93 - - "": [t: "pionowe przedłużenie dzielenia syntetycznego"] # 0xec94 - - "": [t: "lewy przedłużacz sufitu i podłogi"] # 0xec95 - - "": [t: "prawy przedłużacz sufitu i podłogi"] # 0xec96 - - "": [t: "przedłużenie przez nawias kwadratowy"] # 0xec97 - - "": [t: "pionowe przedłużenie kreski"] # 0xec98 - - "": [t: "lewy podwójny pionowe przedłużenie kreski"] # 0xec99 - - "": [t: "poziome przedłużenie kreski"] # 0xec9a - - "": [t: "przedłużenie pod nawiasem kwadratowym"] # 0xec9c - - "": [t: "prawa część dolnego nawiasu"] # 0xec9d - - "": [t: "przedłużenie dolnego nawiasu"] # 0xec9e - - "": [t: "lewa część dolnego nawiasu"] # 0xec9f - - "": [t: "przedłużenie górnego nawiasu klamrowego"] # 0xeca0 - - "": [t: "lewa część górnego nawiasu"] # 0xeca1 - - "": [t: "przedłużenie górnego nawiasu"] # 0xeca2 - - "": [t: "prawa część górnego nawiasu"] # 0xeca3 - - "": [t: "przedłużenie dolnego nawiasu klamrowego"] # 0xeca4 - - "": [t: "stała Plancka przez dwa pi kreska"] # 0xed00 - - "": [t: "lustrzane g"] # 0xed01 - - "": [t: "j bez kropki"] # 0xed02 + - "㏘": [T: "pikometry"] # 0x33d8 + - "㏙": [T: "części na milion"] # 0x33d9 + - "㏚": [T: "petarentgeny"] # 0x33da + - "㏛": [T: "steradiany"] # 0x33db + - "㏜": [T: "siwerty"] # 0x33dc + - "㏝": [T: "webery"] # 0x33dd + - "㏞": [T: "wolty na metr"] # 0x33de + - "㏟": [T: "ampery na metr"] # 0x33df + - "㏿": [T: "galony"] # 0x33ff + - "": [T: "równa się z daszkiem pod"] # 0xe900 + - "": [T: "równa się z plusem nad"] # 0xe901 + - "": [T: "równa się z plusem pod"] # 0xe902 + - "": [T: "tylda z plusem nad"] # 0xe903 + - "": [T: "tylda z plusem pod"] # 0xe904 + - "": [T: "równe podwójny przez większe niż"] # 0xe908 + - "": [T: "równe podwójny przez mniejsze niż"] # 0xe909 + - "": [T: "zawiera lub równe"] # 0xe90a + - "": [T: "nadzbiór lub równy"] # 0xe90b + - "": [T: "podzbiór lub równy"] # 0xe90c + - "": [T: "równe przez mniejsze niż"] # 0xe90d + - "": [T: "należy do lub równe"] # 0xe912 + - "": [T: "równe lub większe niż"] # 0xe913 + - "": [T: "przybliżone nadzbiór"] # 0xe914 + - "": [T: "przybliżone podzbiór"] # 0xe915 + - "": [T: "nadzbiór z kropką zawiera jako relację indeksu dolnego"] # 0xe916 + - "": [T: "podzbiór z kropką jest zawarty jako relacja indeksu dolnego"] # 0xe917 + - "": [T: "równe z kropką pod"] # 0xe918 + - "": [T: "lewy kropka nad minus przez prawy kropka"] # 0xe919 + - "": [T: "prawy kropka nad minus przez lewy kropka"] # 0xe91a + - "": [T: "w przybliżeniu równe minus"] # 0xe91f + - "": [T: "podwójna kwadratowa suma zbiorów"] # 0xe920 + - "": [T: "podwójny kwadrat wielka"] # 0xe921 + - "": [T: "mniejsze niż równe lub większe niż"] # 0xe922 + - "": [T: "tylda z kropką"] # 0xe924 + - "": [T: "tylda z dwiema kropkami"] # 0xe925 + - "": [T: "mniejsze niż większe lub równe"] # 0xe926 + - "": [T: "większe niż mniejsze lub równe"] # 0xe927 + - "": [T: "Równoważne lub mniejsze niż"] # 0xe928 + - "": [T: "Równoważne lub większe niż"] # 0xe929 + - "": [T: "lewy otwórz ramka operator"] # 0xe92a + - "": [T: "prawy otwórz ramka operator"] # 0xe92b + - "": [T: "identyczne z kropką"] # 0xe92c + - "": [T: "większe niż równe lub mniejsze niż"] # 0xe92d + - "": [T: "kreska operator"] # 0xe92e + - "": [T: "podwójny kreska operator"] # 0xe92f + - "": [T: "potrójny kreska operator"] # 0xe930 + - "": [T: "mniejsze niż lub w przybliżeniu równe"] # 0xe932 + - "": [T: "większe niż lub w przybliżeniu równe"] # 0xe933 + - "": [T: "zagnieżdżone mniejsze niż"] # 0xe936 + - "": [T: "zagnieżdżone większe niż"] # 0xe937 + - "": [T: "poprzedza lub Równoważne"] # 0xe93a + - "": [T: "następuje po lub Równoważne"] # 0xe93b + - "": [T: "poprzedza nad równe"] # 0xe940 + - "": [T: "następuje po nad równe"] # 0xe941 + - "": [T: "mniejsze niż nad ukośnie równe nad większe niż"] # 0xe942 + - "": [T: "większe niż nad ukośnie równe nad mniejsze niż"] # 0xe943 + - "": [T: "spełnione przez"] # 0xe948 + - "": [T: "leniwe s"] # 0xe949 + - "": [T: "nie asercja"] # 0xe94a + - "": [T: "podwójny równe"] # 0xe94b + - "": [T: "potrójny równe"] # 0xe94c + - "": [T: "reguła opóźniona"] # 0xe94d + - "": [T: "alias ogranicznika"] # 0xe94e + - "": [T: "normalna podgrupa z kreską"] # 0xe950 + - "": [T: "zawiera jako normalny podgrupa z kreską"] # 0xe951 + - "": [T: "zaokrąglone implikuje"] # 0xe954 + - "": [T: "uśmiech pod kreską"] # 0xe955 + - "": [T: "smutek przez kreskę"] # 0xe956 + - "": [T: "nadzbiór lub w przybliżeniu równe"] # 0xe957 + - "": [T: "podzbiór lub w przybliżeniu równe"] # 0xe958 + - "": [T: "większe niż w przybliżeniu równe lub mniejsze niż"] # 0xe959 + - "": [T: "mniejsze niż w przybliżeniu równe lub większe niż"] # 0xe95a + - "": [T: "podwójny logiczne lub"] # 0xe95c + - "": [T: "podwójna koniunkcja logiczna"] # 0xe95d + - "": [T: "alternatywa logiczna z podwójną kreską pod"] # 0xe95e + - "": [T: "logiczne lub z kreską pod"] # 0xe95f + - "": [T: "w przybliżeniu równe nad równe"] # 0xe962 + - "": [T: "trójkąt skierowany w lewo z kreską dzielącą"] # 0xe964 + - "": [T: "trójkąt skierowany w prawo z kreską dzielącą"] # 0xe965 + - "": [T: "równa się z kropkowaną górną linią"] # 0xe966 + - "": [T: "poprzedza z dwukropkiem"] # 0xe967 + - "": [T: "następuje po z dwukropkiem"] # 0xe968 + - "": [T: "mniejsze niż lub ukośnie równe"] # 0xe969 + - "": [T: "większe niż lub ukośnie równe"] # 0xe96a + - "": [T: "zagnieżdżone bardzo znacznie mniejsze niż"] # 0xe96b + - "": [T: "zagnieżdżone bardzo znacznie większe niż"] # 0xe96c + - "": [T: "wariant różnicy między"] # 0xe96d + - "": [T: "mniejsze niż większe niż nakładka"] # 0xe96e + - "": [T: "alternatywa logiczna z nałożoną koniunkcją logiczną"] # 0xe96f + - "": [T: "nadzbiór przez nadzbiór"] # 0xe970 + - "": [T: "podzbiór przez podzbiór"] # 0xe971 + - "": [T: "nadzbiór przez podzbiór"] # 0xe972 + - "": [T: "podzbiór przez nadzbiór"] # 0xe973 + - "": [T: "potrójna pionowa kreska"] # 0xe979 + - "": [T: "sparowane poczwórne pionowe kropki"] # 0xe97a + - "": [T: "prostopadłe przez kreskę"] # 0xe97b + - "": [T: "lewy znak dedukcji z podwójną pionową kreską"] # 0xe97c + - "": [T: "podwójny lewy znak dedukcji z podwójną pionową kreską"] # 0xe97d + - "": [T: "prostopadłe przez odwrócony prostopadłe"] # 0xe97e + - "": [T: "podwójny lewy znak dedukcji z pionową kreską"] # 0xe97f + - "": [T: "kąt sferyczny otwarty w górę"] # 0xe980 + - "": [T: "podwójny ukośnik"] # 0xe981 + - "": [T: "kąt prosty z narożnikiem"] # 0xe982 + - "": [T: "pionowa kreska w kółku"] # 0xe984 + - "": [T: "znak dzielenia w kółku"] # 0xe985 + - "": [T: "kreskowany ukośnik"] # 0xe986 + - "": [T: "kreskowany ukośnik odwrotny"] # 0xe987 + - "": [T: "kreskowana linia środkowa"] # 0xe988 + - "": [T: "kreskowana pionowa kreska"] # 0xe989 + - "": [T: "prostopadłe z s"] # 0xe98a + - "": [T: "kąt z s"] # 0xe98b + - "": [T: "kąt sferyczny otwarty w lewo"] # 0xe98c + - "": [T: "kąt otwarty w lewo"] # 0xe98d + - "": [T: "pionowa kreska z podwójnym haczykiem"] # 0xe98e + - "": [T: "operator średniej kropki wolnego rodnika"] # 0xe98f + - "": [T: "biały trójkąt skierowany w górę nad kreską"] # 0xe990 + - "": [T: "identyczne i równoległe do"] # 0xe991 + - "": [T: "iloczyn spłaszczony"] # 0xe992 + - "": [T: "potrójny operator kreski z kreską poziomą"] # 0xe993 + - "": [T: "identyczne z podwójnym ukośnikiem"] # 0xe994 + - "": [T: "potrójne skrzyżowane kreski"] # 0xe995 + - "": [T: "pionowa kreska przez okrąg"] # 0xe996 + - "": [T: "pionowy proporcjonalne do"] # 0xe997 + - "": [T: "czarna ostatnia kwadra księżyca"] # 0xe998 + - "": [T: "czarna pierwsza kwadra księżyca"] # 0xe999 + - "": [T: "ujemna fala sinusoidalna"] # 0xe9a0 + - "": [T: "kropka w nawiasach"] # 0xe9a1 + - "": [T: "nawiasy"] # 0xe9a2 + - "": [T: "biały uśmiech"] # 0xe9a3 + - "": [T: "biały smutek"] # 0xe9a4 + - "": [T: "sześciokąt"] # 0xe9a5 + - "": [T: "Równoważne nad plus"] # 0xe9a6 + - "": [T: "plus przez Równoważne"] # 0xe9a7 + - "": [T: "część wspólna z szeryfami"] # 0xe9b0 + - "": [T: "suma zbiorów z szeryfami"] # 0xe9b1 + - "": [T: "kwadratowa część wspólna z szeryfami"] # 0xe9b2 + - "": [T: "kwadratowa suma zbiorów z szeryfami"] # 0xe9b3 + - "": [T: "poprzedza Równoważne lub następuje po"] # 0xe9e0 + - "": [T: "następuje po Równoważne lub poprzedza"] # 0xe9e1 + - "": [T: "poprzedza w przybliżeniu równe lub następuje po"] # 0xe9e2 + - "": [T: "następuje po w przybliżeniu równe lub poprzedza"] # 0xe9e3 + - "": [T: "mniejsze niż Równoważne lub większe niż"] # 0xe9f0 + - "": [T: "większe niż Równoważne lub mniejsze niż"] # 0xe9f1 + - "": [T: "pionowo przekreślone znacznie mniejsze niż"] # 0xea00 + - "": [T: "pionowo przekreślone znacznie większe niż"] # 0xea01 + - "": [T: "nie jest znacznie mniejsze niż, wariant"] # 0xea02 + - "": [T: "nie jest znacznie większe niż, wariant"] # 0xea03 + - "": [T: "mniejsze niż z pionową kreską i nie podwójne równe"] # 0xea04 + - "": [T: "większe niż z pionową kreską i nie podwójne równe"] # 0xea05 + - "": [T: "nie mniejsze lub równe"] # 0xea06 + - "": [T: "nie większe lub równe"] # 0xea07 + - "": [T: "ani równe, ani mniejsze niż"] # 0xea09 + - "": [T: "nie zawiera lub równe"] # 0xea0a + - "": [T: "ani nadzbiór, ani równe"] # 0xea0b + - "": [T: "ani podzbiór, ani równe"] # 0xea0c + - "": [T: "ukośnik odwrotny z podzbiorem"] # 0xea0d + - "": [T: "ani równe, ani większe niż"] # 0xea0e + - "": [T: "nie minus operator tyldy"] # 0xea0f + - "": [T: "ani równe, ani mniejsze niż"] # 0xea10 + - "": [T: "nie operator tyldy"] # 0xea11 + - "": [T: "nie jest elementem zbioru lub równe"] # 0xea12 + - "": [T: "ani równe, ani większe niż"] # 0xea13 + - "": [T: "nie w przybliżeniu równe"] # 0xea14 + - "": [T: "nie następuje po podobne"] # 0xea15 + - "": [T: "mniejsze niż lub ukośnie równe z ukośnikiem"] # 0xea16 + - "": [T: "większe niż lub ukośnie równe z ukośnikiem"] # 0xea17 + - "": [T: "nadzbiór ukośnik"] # 0xea1a + - "": [T: "nie zawiera"] # 0xea1b + - "": [T: "nie mniejsze lub równe"] # 0xea1d + - "": [T: "nie większe lub równe"] # 0xea1e + - "": [T: "nie w przybliżeniu równe minus"] # 0xea1f + - "": [T: "zanegowana przynależność do zbioru z kropką nad"] # 0xea22 + - "": [T: "nie kąt pionowy"] # 0xea2c + - "": [T: "nie ukośnie równoległe"] # 0xea2d + - "": [T: "nie kreska operator"] # 0xea2e + - "": [T: "nie podwójny kreska operator"] # 0xea2f + - "": [T: "nie potrójny kreska operator"] # 0xea30 + - "": [T: "mniejsze niż ale nie w przybliżeniu równe"] # 0xea32 + - "": [T: "większe niż ale nie w przybliżeniu równe"] # 0xea33 + - "": [T: "mniejsze niż lub nie równa się"] # 0xea34 + - "": [T: "większe niż lub nie równa się"] # 0xea35 + - "": [T: "nie zagnieżdżone mniejsze niż"] # 0xea36 + - "": [T: "nie zagnieżdżone większe niż"] # 0xea37 + - "": [T: "nie jest znacznie mniejsze niż"] # 0xea38 + - "": [T: "nie jest znacznie większe niż"] # 0xea39 + - "": [T: "poprzedza ale nie Równoważne"] # 0xea3a + - "": [T: "następuje po ale nie Równoważne"] # 0xea3b + - "": [T: "poprzedza ale nie równa się"] # 0xea3c + - "": [T: "następuje po ale nie równa się"] # 0xea3d + - "": [T: "nie równa się ani nie poprzedza"] # 0xea3e + - "": [T: "nie równa się ani nie następuje po"] # 0xea3f + - "": [T: "poprzedza ale nie równa się"] # 0xea40 + - "": [T: "następuje po ale nie równa się"] # 0xea41 + - "": [T: "ani podzbiór, ani równe"] # 0xea42 + - "": [T: "ani nadzbiór, ani równe"] # 0xea43 + - "": [T: "podzbiór lub nie równa się"] # 0xea44 + - "": [T: "nadzbiór lub nie równa się"] # 0xea45 + - "": [T: "ani podzbiór, ani równe"] # 0xea46 + - "": [T: "ani nadzbiór, ani równe"] # 0xea47 + - "": [T: "nie potrójny mniejsze niż"] # 0xea48 + - "": [T: "nie potrójny większe niż"] # 0xea49 + - "": [T: "nie poprzedza równa się"] # 0xea4c + - "": [T: "nie następuje po równa się"] # 0xea4d + - "": [T: "nie normalna podgrupa z kreską"] # 0xea50 + - "": [T: "nie zawiera jako normalnej podgrupy z kreską"] # 0xea51 + - "": [T: "nie różnica między"] # 0xea52 + - "": [T: "nie geometrycznie równoważne"] # 0xea53 + - "": [T: "nie pionowo podobne"] # 0xea54 + - "": [T: "nie równe lub podobne"] # 0xea55 + - "": [T: "nie pionowo przybliżone"] # 0xea56 + - "": [T: "nie w przybliżeniu identyczne z"] # 0xea57 + - "": [T: "nie wyboiście równe"] # 0xea58 + - "": [T: "nie wyboiście pojedynczo równe"] # 0xea59 + - "": [T: "nie równe kropka"] # 0xea5a + - "": [T: "odwrotne nie równoważne"] # 0xea5b + - "": [T: "nie kwadrat podzbiór"] # 0xea60 + - "": [T: "nie kwadrat nadzbiór"] # 0xea61 + - "": [T: "nie w przybliżeniu równe nad znakiem równości"] # 0xea62 + - "": [T: "nie ściśle równoważne"] # 0xea63 + - "": [T: "nie przystające z kropką"] # 0xea64 + - "": [T: "odwrotnie nie równe"] # 0xea65 + - "": [T: "nie pionowy lewy trójkąt równa się"] # 0xea70 + - "": [T: "nie pionowy prawy trójkąt równa się"] # 0xea71 + - "": [T: "nie cząstkowe"] # 0xea80 + - "": [T: "ozdobnik przedłużenia strzałki"] # 0xeb00 + - "": [T: "strzałka prawy przeze strzałką lewy"] # 0xeb01 + - "": [T: "strzałka prawy przeze strzałką lewy"] # 0xeb02 + - "": [T: "harpun prawy przez harpun lewy"] # 0xeb03 + - "": [T: "harpun prawy przez harpun lewy"] # 0xeb04 + - "": [T: "podwójna strzałka północny wschód południowy zachód"] # 0xeb05 + - "": [T: "podwójna strzałka północny zachód południowy wschód"] # 0xeb06 + - "": [T: "poziome przedłużenie harpuna"] # 0xeb07 + - "": [T: "łuk ze strzałką w lewo przeciwnie do ruchu wskazówek zegara"] # 0xeb08 + - "": [T: "łuk ze strzałką w prawo przeciwnie do ruchu wskazówek zegara"] # 0xeb09 + - "": [T: "duży strzałka w prawo akcent"] # 0xeb0b + - "": [T: "duży strzałka w lewo akcent"] # 0xeb0c + - "": [T: "lewy grot strzałki"] # 0xeb0d + - "": [T: "prawy grot strzałki"] # 0xeb0e + - "": [T: "duża strzałka w prawo z lewym przekreśleniem"] # 0xeb0f + - "": [T: "poziome przedłużenie podwójnej strzałki"] # 0xeb10 + - "": [T: "duża podwójna strzałka w lewo i prawo z przekreśleniem"] # 0xeb11 + - "": [T: "strzałka w dół po lewej i strzałka w górę"] # 0xeb12 + - "": [T: "strzałka w lewo z dolnym narożnikiem"] # 0xeb13 + - "": [T: "strzałka w prawo z górnym narożnikiem"] # 0xeb14 + - "": [T: "strzałka w lewo z górnym narożnikiem"] # 0xeb15 + - "": [T: "strzałka po górnym półokręgu przeciwnie do ruchu wskazówek zegara z plusem"] # 0xeb16 + - "": [T: "strzałka po górnym półokręgu zgodnie z ruchem wskazówek zegara z minusem"] # 0xeb17 + - "": [T: "strzałka w prawo z ogonem i przekreśleniem"] # 0xeb18 + - "": [T: "prawy harpun dolny"] # 0xeb19 + - "": [T: "lewy harpun dolny"] # 0xeb1a + - "": [T: "lewy prawy harpun dolny"] # 0xeb1b + - "": [T: "lewy prawy harpun górny"] # 0xeb1c + - "": [T: "górny dolny harpun lewy"] # 0xeb1d + - "": [T: "górny dolny harpun prawy"] # 0xeb1e + - "": [T: "strzałka w górę w prawo ze strzałką w dół"] # 0xeb1f + - "": [T: "lewy harpun do kreski z grotem górnym"] # 0xeb20 + - "": [T: "prawy harpun do kreski z grotem górnym"] # 0xeb21 + - "": [T: "lewy harpun do kreski z grotem dolnym"] # 0xeb22 + - "": [T: "prawy harpun do kreski z grotem dolnym"] # 0xeb23 + - "": [T: "lewy harpun od kreski z grotem górnym"] # 0xeb24 + - "": [T: "prawy harpun od kreski z grotem górnym"] # 0xeb25 + - "": [T: "lewy harpun od kreski z grotem dolnym"] # 0xeb26 + - "": [T: "prawy harpun od kreski z grotem dolnym"] # 0xeb27 + - "": [T: "górny harpun do kreski z grotem lewym"] # 0xeb28 + - "": [T: "dolny harpun do kreski z grotem lewym"] # 0xeb29 + - "": [T: "górny harpun do kreski z grotem prawym"] # 0xeb2a + - "": [T: "dolny harpun do kreski z grotem prawym"] # 0xeb2b + - "": [T: "górny harpun od kreski z grotem lewym"] # 0xeb2c + - "": [T: "dolny harpun od kreski z grotem lewym"] # 0xeb2d + - "": [T: "górny harpun od kreski z grotem prawym"] # 0xeb2e + - "": [T: "dolny harpun od kreski z grotem prawym"] # 0xeb2f + - "": [T: "strzałka w górę do kreski"] # 0xeb30 + - "": [T: "strzałka w dół do kreski"] # 0xeb31 + - "": [T: "górny harpun w lewo z dolnym harpunem"] # 0xeb32 + - "": [T: "górny harpun w prawo z dolnym harpunem"] # 0xeb33 + - "": [T: "górny grot strzałki"] # 0xeb34 + - "": [T: "dolny grot strzałki"] # 0xeb35 + - "": [T: "podwójny harpun z lewym grotem dolnym i prawym grotem górnym"] # 0xeb36 + - "": [T: "podwójny harpun z lewym grotem górnym i prawym grotem dolnym"] # 0xeb37 + - "": [T: "strzałka w lewo przez kreskę"] # 0xeb38 + - "": [T: "strzałka w prawo przez kreskę"] # 0xeb39 + - "": [T: "strzałka w lewo pod kreski"] # 0xeb3a + - "": [T: "strzałka w prawo pod kreski"] # 0xeb3b + - "": [T: "lewy prawy potrójny strzałka"] # 0xeb3c + - "": [T: "podwójna strzałka północny wschód południowy wschód"] # 0xeb3f + - "": [T: "strzałka po lewym półokręgu przeciwnie do ruchu wskazówek zegara"] # 0xeb40 + - "": [T: "strzałka po lewym półokręgu zgodnie z ruchem wskazówek zegara"] # 0xeb41 + - "": [T: "lewy otwarty okrąg lewy strzałka w prawo"] # 0xeb42 + - "": [T: "strzałka w prawo przez tylda"] # 0xeb44 + - "": [T: "strzałka w lewo przez tylda"] # 0xeb45 + - "": [T: "lewy harpun przez kreskę"] # 0xeb48 + - "": [T: "prawy harpun przez kreskę"] # 0xeb49 + - "": [T: "lewy harpun pod kreski"] # 0xeb4a + - "": [T: "prawy harpun pod kreski"] # 0xeb4b + - "": [T: "krótka czarna strzałka w lewo"] # 0xeb4c + - "": [T: "strzałka po prawym półokręgu zgodnie z ruchem wskazówek zegara"] # 0xeb50 + - "": [T: "strzałka po prawym półokręgu przeciwnie do ruchu wskazówek zegara"] # 0xeb51 + - "": [T: "lewy otwarty okrąg lewy prawy harpun"] # 0xeb52 + - "": [T: "strzałka w górę lewy z pionową kreską"] # 0xeb58 + - "": [T: "strzałka w dół lewy z pionową kreską"] # 0xeb59 + - "": [T: "strzałka w górę prawy z pionową kreską"] # 0xeb5a + - "": [T: "strzałka w dół prawy z pionową kreską"] # 0xeb5b + - "": [T: "strzałka w prawo z wydłużonym dolnym hakiem"] # 0xeb5c + - "": [T: "strzałka w lewo z wydłużonym hakiem"] # 0xeb5d + - "": [T: "strzałka w lewo z wydłużonym dolnym hakiem"] # 0xeb5e + - "": [T: "strzałka w prawo z wydłużonym hakiem"] # 0xeb5f + - "": [T: "nie strzałka w prawo falista"] # 0xeb60 + - "": [T: "nie strzałka w prawo zakrzywiony"] # 0xeb61 + - "": [T: "górny harpun lewy z pionową kreską"] # 0xeb68 + - "": [T: "dolny harpun lewy z pionową kreską"] # 0xeb69 + - "": [T: "górny harpun prawy z pionową kreską"] # 0xeb6a + - "": [T: "dolny harpun prawy z pionową kreską"] # 0xeb6b + - "": [T: "pionowe przedłużenie podwójnej strzałki"] # 0xeb6c + - "": [T: "pionowe przedłużenie harpuna z grotem w lewo"] # 0xeb6d + - "": [T: "pionowe przedłużenie harpuna z grotem w prawo"] # 0xeb6e + - "": [T: "prawy harpun przez lewy harpun prawy"] # 0xeb6f + - "": [T: "prawy harpun przez lewy harpun lewy"] # 0xeb70 + - "": [T: "lewy harpun przez prawy harpun prawy"] # 0xeb71 + - "": [T: "lewy harpun przez prawy harpun lewy"] # 0xeb72 + - "": [T: "strzałka w lewo od kreski z grotem strzałki"] # 0xeb73 + - "": [T: "przedłużenie strzałki w prawo od lewej kreski"] # 0xeb74 + - "": [T: "strzałka w lewo od kreski ogon"] # 0xeb75 + - "": [T: "strzałka w prawo od kreski ogon"] # 0xeb76 + - "": [T: "strzałka w prawo od kreski z grotem strzałki"] # 0xeb77 + - "": [T: "górny harpun od kreski z lewym grotem strzałki"] # 0xeb78 + - "": [T: "strzałka w prawo nad strzałką w lewo prawy"] # 0xeb79 + - "": [T: "strzałka w prawo nad strzałką w lewo lewy"] # 0xeb7a + - "": [T: "strzałka w lewo nad strzałką w prawo prawy"] # 0xeb7b + - "": [T: "strzałka w lewo nad strzałką w prawo lewy"] # 0xeb7c + - "": [T: "strzałka w górę od kreski z grotem strzałki"] # 0xeb7d + - "": [T: "strzałka w górę od kreski ogon"] # 0xeb7e + - "": [T: "strzałka w dół od kreski ogon"] # 0xeb7f + - "": [T: "strzałka w dół od kreski z grotem strzałki"] # 0xeb80 + - "": [T: "dolny harpun od kreski z prawym grotem strzałki"] # 0xeb81 + - "": [T: "górny harpun w lewo z dolnym harpunem dolny"] # 0xeb82 + - "": [T: "przedłużenie górnego harpuna w lewo i dolnego harpuna"] # 0xeb83 + - "": [T: "dolny harpun w lewo z górnym harpunem górny"] # 0xeb84 + - "": [T: "górny harpun w lewo z dolnym harpunem górny"] # 0xeb85 + - "": [T: "przedłużenie dolnego harpuna w lewo i górnego harpuna"] # 0xeb86 + - "": [T: "dolny harpun w lewo z górnym harpunem dolny"] # 0xeb87 + - "": [T: "strzałka w górę po lewej i strzałka w dół dolny"] # 0xeb88 + - "": [T: "strzałka w dół po lewej i strzałka w górę górny"] # 0xeb89 + - "": [T: "strzałka w górę po lewej i strzałka w dół górny"] # 0xeb8a + - "": [T: "strzałka w dół po lewej i strzałka w górę dolny"] # 0xeb8b + - "": [T: "przedłużenie strzałki w lewo i w prawo"] # 0xeb8c + - "": [T: "przedłużenie strzałki północno-wschodniej"] # 0xeb8d + - "": [T: "przedłużenie strzałki północno-zachodniej"] # 0xeb8e + - "": [T: "lewa część dolnego nawiasu klamrowego"] # 0xec00 + - "": [T: "środkowa część dolnego nawiasu klamrowego"] # 0xec01 + - "": [T: "prawa część dolnego nawiasu klamrowego"] # 0xec02 + - "": [T: "poziome przedłużenie nawiasu klamrowego"] # 0xec03 + - "": [T: "lewa część górnego nawiasu klamrowego"] # 0xec04 + - "": [T: "środkowa część górnego nawiasu klamrowego"] # 0xec05 + - "": [T: "prawa część górnego nawiasu klamrowego"] # 0xec06 + - "": [T: "lewa pionowa kreska"] # 0xec07 + - "": [T: "prawa pionowa kreska"] # 0xec08 + - "": [T: "lewa podwójna pionowa kreska"] # 0xec09 + - "": [T: "prawa podwójna pionowa kreska"] # 0xec0a + - "": [T: "poziome przedłużenie nawiasu kwadratowego"] # 0xec0b + - "": [T: "dolny nawias kwadratowy"] # 0xec0c + - "⎵": [T: "dolny nawias kwadratowy"] # 0x23b5 + - "": [T: "górny nawias kwadratowy"] # 0xec0d + - "⎴": [T: "górny nawias kwadratowy"] # 0x23b4 + - "": [T: "pod nawias kwadratowy lewy"] # 0xec0e + - "": [T: "pod nawias kwadratowy prawy"] # 0xec0f + - "": [T: "przez nawias kwadratowy lewy"] # 0xec10 + - "": [T: "przez nawias kwadratowy prawy"] # 0xec11 + - "": [T: "lewy nawias okrągły 1"] # 0xec12 + - "": [T: "lewy nawias okrągły 2"] # 0xec13 + - "": [T: "lewy nawias okrągły 3"] # 0xec14 + - "": [T: "lewy nawias okrągły 4"] # 0xec15 + - "": [T: "prawy nawias okrągły 1"] # 0xec16 + - "": [T: "prawy nawias okrągły 2"] # 0xec17 + - "": [T: "prawy nawias okrągły 3"] # 0xec18 + - "": [T: "prawy nawias okrągły 4"] # 0xec19 + - "": [T: "znak pierwiastka 1"] # 0xec1a + - "": [T: "znak pierwiastka 2"] # 0xec1b + - "": [T: "znak pierwiastka 3"] # 0xec1c + - "": [T: "znak pierwiastka 4"] # 0xec1d + - "": [T: "znak pierwiastka 5"] # 0xec1e + - "": [T: "dolna część znaku pierwiastka"] # 0xec1f + - "": [T: "pionowe przedłużenie znaku pierwiastka"] # 0xec20 + - "": [T: "górna część znaku pierwiastka"] # 0xec21 + - "": [T: "lewy biały nawias kwadratowy górny"] # 0xec22 + - "": [T: "przedłużenie lewego białego nawiasu kwadratowego"] # 0xec23 + - "": [T: "lewy biały nawias kwadratowy dolny"] # 0xec24 + - "": [T: "prawy biały nawias kwadratowy górny"] # 0xec25 + - "": [T: "przedłużenie prawego białego nawiasu kwadratowego"] # 0xec26 + - "": [T: "prawy biały nawias kwadratowy dolny"] # 0xec27 + - "": [T: "lewy biały zakrzywiony nawias kwadratowy"] # 0xec30 + - "": [T: "prawy biały zakrzywiony nawias kwadratowy"] # 0xec31 + - "": [T: "znak dzielenia pisemnego"] # 0xec32 + - "": [T: "przedłużenie znaku dzielenia pisemnego"] # 0xec33 + - "": [T: "krótkie dzielenie"] # 0xec34 + - "": [T: "podwójne wiązanie em od południowego zachodu do północnego wschodu"] # 0xec40 + - "": [T: "podwójne wiązanie em od północnego zachodu do południowego wschodu"] # 0xec41 + - "": [T: "pojedyncze poziome wiązanie em"] # 0xec42 + - "": [T: "podwójne poziome wiązanie em"] # 0xec43 + - "": [T: "potrójne poziome wiązanie em"] # 0xec44 + - "": [T: "pojedyncze pionowe wiązanie em"] # 0xec45 + - "": [T: "podwójne pionowe wiązanie em"] # 0xec46 + - "": [T: "potrójne pionowe wiązanie em"] # 0xec47 + - "": [T: "wiązanie em mniejsze niż"] # 0xec48 + - "": [T: "wiązanie em większe niż"] # 0xec49 + - "": [T: "pojedyncze poziome wiązanie en"] # 0xec4a + - "": [T: "podwójne poziome wiązanie en"] # 0xec4b + - "": [T: "potrójne poziome wiązanie en"] # 0xec4c + - "": [T: "górny lewy prostokąt"] # 0xec80 + - "": [T: "dolny lewy prostokąt"] # 0xec81 + - "": [T: "górny prawy prostokąt"] # 0xec90 + - "": [T: "dolny prawy prostokąt"] # 0xec91 + - "": [T: "narożnik dzielenia syntetycznego"] # 0xec92 + - "": [T: "poziome przedłużenie dzielenia syntetycznego"] # 0xec93 + - "": [T: "pionowe przedłużenie dzielenia syntetycznego"] # 0xec94 + - "": [T: "lewy przedłużacz sufitu i podłogi"] # 0xec95 + - "": [T: "prawy przedłużacz sufitu i podłogi"] # 0xec96 + - "": [T: "przedłużenie przez nawias kwadratowy"] # 0xec97 + - "": [T: "pionowe przedłużenie kreski"] # 0xec98 + - "": [T: "lewy podwójny pionowe przedłużenie kreski"] # 0xec99 + - "": [T: "poziome przedłużenie kreski"] # 0xec9a + - "": [T: "przedłużenie pod nawiasem kwadratowym"] # 0xec9c + - "": [T: "prawa część dolnego nawiasu"] # 0xec9d + - "": [T: "przedłużenie dolnego nawiasu"] # 0xec9e + - "": [T: "lewa część dolnego nawiasu"] # 0xec9f + - "": [T: "przedłużenie górnego nawiasu klamrowego"] # 0xeca0 + - "": [T: "lewa część górnego nawiasu"] # 0xeca1 + - "": [T: "przedłużenie górnego nawiasu"] # 0xeca2 + - "": [T: "prawa część górnego nawiasu"] # 0xeca3 + - "": [T: "przedłużenie dolnego nawiasu klamrowego"] # 0xeca4 + - "": [T: "stała Plancka przez dwa pi kreska"] # 0xed00 + - "": [T: "lustrzane g"] # 0xed01 + - "": [T: "j bez kropki"] # 0xed02 - "": [T: "digamma"] # 0xed03 - "ϝ": [T: "digamma"] # 0x3dd - "": [T: "d"] # 0xed10 @@ -3004,8 +3004,8 @@ - spell: "translate('.', 'ⅅ', 'DD')" # 0xed16, 0x2145 # The private use chars are from MathType - - "": [t: "całka krzywoliniowa z pętlą przeciwnie do ruchu wskazówek zegara"] # 0xee00 - - "": [t: "całka krzywoliniowa z pętlą zgodnie z ruchem wskazówek zegara"] # 0xee01 + - "": [T: "całka krzywoliniowa z pętlą przeciwnie do ruchu wskazówek zegara"] # 0xee00 + - "": [T: "całka krzywoliniowa z pętlą zgodnie z ruchem wskazówek zegara"] # 0xee01 - "": [T: ""] # 0xee04 - "": [T: ""] # 0xee05 - "": [T: ""] # 0xee06 @@ -3015,30 +3015,30 @@ - "": [T: ""] # 0xee0a - "": [T: ""] # 0xee0b - "": [T: ""] # 0xee0c - - "": [t: "ozdobnik statusu połączenia"] # 0xee0d - - "": [t: "lewy ozdobnik statusu połączenia"] # 0xee0e - - "": [t: "prawy ozdobnik statusu połączenia"] # 0xee0f - - "": [t: "przedłużenie ozdobnika statusu połączenia"] # 0xee10 - - "": [t: "całka z pętlą"] # 0xee11 - - "": [t: "podwójna całka z pętlą"] # 0xee12 - - "": [t: "potrójna całka z pętlą"] # 0xee13 - - "": [t: "rozszerzająca się podwójna całka z pętlą"] # 0xee15 - - "": [t: "rozszerzająca się potrójna całka z pętlą"] # 0xee16 + - "": [T: "ozdobnik statusu połączenia"] # 0xee0d + - "": [T: "lewy ozdobnik statusu połączenia"] # 0xee0e + - "": [T: "prawy ozdobnik statusu połączenia"] # 0xee0f + - "": [T: "przedłużenie ozdobnika statusu połączenia"] # 0xee10 + - "": [T: "całka z pętlą"] # 0xee11 + - "": [T: "podwójna całka z pętlą"] # 0xee12 + - "": [T: "potrójna całka z pętlą"] # 0xee13 + - "": [T: "rozszerzająca się podwójna całka z pętlą"] # 0xee15 + - "": [T: "rozszerzająca się potrójna całka z pętlą"] # 0xee16 - "": [T: "akcent asymptotycznie równy"] # 0xee17 - - "": [t: "akcent znaku równości"] # 0xee18 - - "": [t: "poczwórny prim"] # 0xee19 - - "": [t: "kreska akcent z otwartym okręgiem po lewej"] # 0xee1a - - "": [t: "kreska akcent z zamkniętym okręgiem po lewej"] # 0xee1b - - "": [t: "kreska akcent z otwartym okręgiem po prawej"] # 0xee1c - - "": [t: "kreska akcent z przekreśloną kropką"] # 0xee1d - - "": [t: "kreska akcent z kropką pod spodem"] # 0xee1e - - "": [t: "kreska akcent z podwójnie przekreśloną kropką"] # 0xee1f - - "": [t: "kreska akcent z podwójną kropką pod spodem"] # 0xee20 - - "": [t: "kreska akcent z daszkiem"] # 0xee21 - - "": [t: "gruby akcent kreski dolnej"] # 0xee22 - - "": [t: "kreska akcent z zamkniętym okręgiem po prawej"] # 0xee23 - - "": [t: "duży kropka nad"] # 0xee24 - - "": [t: "znak wyrównania"] # 0xef00 + - "": [T: "akcent znaku równości"] # 0xee18 + - "": [T: "poczwórny prim"] # 0xee19 + - "": [T: "kreska akcent z otwartym okręgiem po lewej"] # 0xee1a + - "": [T: "kreska akcent z zamkniętym okręgiem po lewej"] # 0xee1b + - "": [T: "kreska akcent z otwartym okręgiem po prawej"] # 0xee1c + - "": [T: "kreska akcent z przekreśloną kropką"] # 0xee1d + - "": [T: "kreska akcent z kropką pod spodem"] # 0xee1e + - "": [T: "kreska akcent z podwójnie przekreśloną kropką"] # 0xee1f + - "": [T: "kreska akcent z podwójną kropką pod spodem"] # 0xee20 + - "": [T: "kreska akcent z daszkiem"] # 0xee21 + - "": [T: "gruby akcent kreski dolnej"] # 0xee22 + - "": [T: "kreska akcent z zamkniętym okręgiem po prawej"] # 0xee23 + - "": [T: "duży kropka nad"] # 0xee24 + - "": [T: "znak wyrównania"] # 0xef00 - "": [T: ""] # 0xef01 - "​": [T: ""] # 0x200b - "": [T: ""] # 0xef02 @@ -3057,110 +3057,110 @@ - "": [T: ""] # 0xef23 - "": [T: ""] # 0xef24 - "": [T: ""] # 0xef29 - - "": [t: "brakujący składnik"] # 0xef41 - - "": [t: "całka krzywoliniowa zgodna z ruchem wskazówek zegara ze strzałką po lewej"] # 0xef80 - - "": [t: "całka z kwadrat"] # 0xef81 - - "": [t: "całka z ukośnikiem"] # 0xef82 - - "": [t: "odwrócony całka"] # 0xef83 - - "": [t: "podwójny zero przez podwójny zero"] # 0xef90 - - "": [t: "zero z ukośnikiem"] # 0xef91 + - "": [T: "brakujący składnik"] # 0xef41 + - "": [T: "całka krzywoliniowa zgodna z ruchem wskazówek zegara ze strzałką po lewej"] # 0xef80 + - "": [T: "całka z kwadrat"] # 0xef81 + - "": [T: "całka z ukośnikiem"] # 0xef82 + - "": [T: "odwrócony całka"] # 0xef83 + - "": [T: "podwójny zero przez podwójny zero"] # 0xef90 + - "": [T: "zero z ukośnikiem"] # 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: "fraktura" + - T: "fraktura" - spell: "translate('.', '𝔄𝔅𝔆𝔇𝔈𝔉𝔊𝔋𝔌𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔕𝔖𝔗𝔘𝔙𝔚𝔛𝔜', 'ABCDEFGHIJKLMNOPQRSTUVWXY')" - "-": # 0xf000 - 0xf018 - - t: "fraktura" + - T: "fraktura" - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXY')" - "𝔞-𝔷": # 0x1d51e - 0x1d537 - - t: "fraktura" + - T: "fraktura" - spell: "translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz')" - "-": # 0xf01a - 0xf033 - - t: "fraktura" + - T: "fraktura" - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" - "𝕬-𝖅": # 0x1D56C - 0x1D585 - - t: "fraktura" - - t: "pogrubione" + - T: "fraktura" + - T: "pogrubione" - spell: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "-": # 0xf040 - 0xf059 - - t: "fraktura" - - t: "pogrubione" + - T: "fraktura" + - T: "pogrubione" - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "𝖆-𝖟": # 0x1d586 - 0x1d59f - - t: "fraktura" - - t: "pogrubione" + - T: "fraktura" + - T: "pogrubione" - spell: "translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz')" - "-": # 0xf05a - 0xf073 - - t: "fraktura" - - t: "pogrubione" + - T: "fraktura" + - T: "pogrubione" - 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: "dwukreskowe" + - T: "dwukreskowe" - spell: "translate('.', '𝔸𝔹𝔺𝔻𝔼𝔽𝔾𝔿𝕀𝕁𝕂𝕃𝕄𝕅𝕆𝕇𝕈𝕉𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "-": # 0xf080 - 0xf098 - - t: "dwukreskowe" + - T: "dwukreskowe" - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "𝕒-𝕫": # 0x1d552 - 0x1d56b - - t: "dwukreskowe" + - T: "dwukreskowe" - spell: "translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz')" - "-": # 0xf09a - 0xf0b3 - - t: "dwukreskowe" + - T: "dwukreskowe" - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" - "𝟘-𝟡": # 0x1d7d8 - 0x1d7e1 - - t: "dwukreskowe" + - T: "dwukreskowe" - spell: "translate('.', '𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡', '0123456789')" - "-": # 0xf0c0 - 0xf0c9 - - t: "dwukreskowe" + - T: "dwukreskowe" - spell: "translate('.', '', '0123456789')" - - "": [t: "dwukreskowe nabla"] # 0xf0ca - - "": [t: "dwukreskowa stała Eulera"] # 0xf0cb + - "": [T: "dwukreskowe nabla"] # 0xf0ca + - "": [T: "dwukreskowa stała Eulera"] # 0xf0cb # script chars in math alphabetic block and also MathType private use area - "𝒜-𝒵": # 0x1d49c - 0x1d4b5 - - t: "pisane" + - T: "pisane" - spell: "translate('.', '𝒜𝒝𝒞𝒟𝒠𝒡𝒢𝒣𝒤𝒥𝒦𝒧𝒨𝒩𝒪𝒫𝒬𝒭𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "-": # 0xf100 - 0xf119 - - t: "pisane" + - T: "pisane" - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "𝒶-𝓏": # 0x1d4b6 - 0x1d4cf - - t: "pisane" + - T: "pisane" - spell: "translate('.', '𝒶𝒷𝒸𝒹𝒺𝒻𝒼𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝓄𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏', 'abcdefghijklmnopqrstuvwxyz')" - "-": # 0xf11a - 0xf133 - - t: "pisane" + - T: "pisane" - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" # bold script chars in math alphabetic block and also MathType private use area - "𝓐-𝓩": # 0x1d4d0 - 0x1d4e9 - - t: "pisane" - - t: "pogrubione" + - T: "pisane" + - T: "pogrubione" - spell: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "-": # 0xf140 - 0xf159 - - t: "pisane" - - t: "pogrubione" + - T: "pisane" + - T: "pogrubione" - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "𝓪-𝔃": # 0x1d4ea - 0x1d503 - - t: "pisane" - - t: "pogrubione" + - T: "pisane" + - T: "pogrubione" - spell: "translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz')" - "-": # 0xf15a - 0xf173 - - t: "pisane" - - t: "pogrubione" + - T: "pisane" + - T: "pogrubione" - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" - "-": # 0xf180 - 0xf199 @@ -3179,11 +3179,11 @@ if: "$SpeechOverrides_CapitalLetters = ''" then_test: if: "$Impairment = 'Blindness'" - then: [t: "wielka"] + then: [T: "wielka"] else: [x: "$SpeechOverrides_CapitalLetters"] - pitch: value: "$CapitalLetters_Pitch" - replace: [t: "ligatura ae"] + replace: [T: "ligatura ae"] - "": # 0xf19b - test: if: "$CapitalLetters_Beep" @@ -3197,11 +3197,11 @@ if: "$SpeechOverrides_CapitalLetters = ''" then_test: if: "$Impairment = 'Blindness'" - then: [t: "wielka"] + then: [T: "wielka"] else: [x: "$SpeechOverrides_CapitalLetters"] - pitch: value: "$CapitalLetters_Pitch" - replace: [t: "eszett"] + replace: [T: "eszett"] - "": # 0xf19c - test: if: "$CapitalLetters_Beep" @@ -3215,38 +3215,38 @@ if: "$SpeechOverrides_CapitalLetters = ''" then_test: if: "$Impairment = 'Blindness'" - then: [t: "wielka"] + then: [T: "wielka"] else: [x: "$SpeechOverrides_CapitalLetters"] - pitch: value: "$CapitalLetters_Pitch" - replace: [t: "o z kreską"] + replace: [T: "o z kreską"] # MathType only has a few of the cap Greek letters in PUA - "": # 0xf201 - 0xf209 - - t: "dwukreskowe" + - T: "dwukreskowe" - spell: "translate('.', '', 'ΔΨΛΠΣΘΓΩΥ')" - "-": # 0xf220 - 0xf236 - - t: "dwukreskowe" + - T: "dwukreskowe" - spell: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" - - "": [t: "dwukreskowe sigma końcowa"] # 0xf237 - - "": [t: "dwukreskowe ro"] # 0xf250 - - "": [t: "dwukreskowe fi"] # 0xf251 + - "": [T: "dwukreskowe sigma końcowa"] # 0xf237 + - "": [T: "dwukreskowe ro"] # 0xf250 + - "": [T: "dwukreskowe fi"] # 0xf251 - "𝐀-𝐙": # 0x1d400 - 0x1d419 - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "-": # 0xf260 - 0xf279 - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "𝐚-𝐳": # 0x1d41a - 0x1d433 - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz')" - "-": # 0xf27a - 0xf293 - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" - "𝐴-𝑍": # 0x1d434 - 0x1d44d @@ -3262,23 +3262,23 @@ - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" - "𝑨-𝒁": # 0x1d468 - 0x1d481 - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "-": # 0xf2c8 - 0xf2e1 - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "𝒂-𝒛": # 0x1d482 - 0x1d49b - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" - "-": # 0xf2e2 - 0xf2fb - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 @@ -3294,53 +3294,53 @@ - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" - "𝗔-𝗭": # 0x1d5d4 - 0x1d5ed - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "-": # 0xf334 - 0xf34d - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "𝗮-𝘇": # 0x1d5ee - 0x1d607 - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" - "-": # 0xf34e - 0xf367 - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" - "𝘈-𝘡": # 0x1d608 - 0x1d621 - # - t: "kursywa" + # - T: "kursywa" - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "-": # 0xf368 - 0xf381 - # - t: "kursywa" + # - T: "kursywa" - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "𝘢-𝘻": # 0x1d622 - 0x1d63b - # - t: "kursywa" + # - T: "kursywa" - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" - "-": # 0xf382 - 0xf39b - # - t: "kursywa" + # - T: "kursywa" - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" - "𝘼-𝙕": # 0x1d63c - 0x1d655 - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "-": # 0xf39c - 0xf3b5 - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" - "𝙖-𝙯": # 0x1d656 - 0x1d66f - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" - "-": # 0xf3b6 - 0xf3cf - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" - "𝙰-𝚉": # 0x1d670 - 0x1d689 @@ -3355,170 +3355,170 @@ - "-": # 0xf3ea - 0xf403 - spell: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" - - "": [t: "bez kropki I"] # 0xf404 - - "𝚤": [t: "bez kropki I"] # 0x1d6a4 - - "𝚥": [t: "j bez kropki"] # 0x1d6a5 + - "": [T: "bez kropki I"] # 0xf404 + - "𝚤": [T: "bez kropki I"] # 0x1d6a4 + - "𝚥": [T: "j bez kropki"] # 0x1d6a5 - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" - "-": # 0xf408 - 0xf420 - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" - "𝛂-𝛚": # 0x1d6c2 - 0x1d6da - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒𝛓𝛔𝛕𝛖𝛗𝛘𝛙𝛚', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "-": # 0xf422 - 0xf43a - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" - - "": [t: "pogrubiona nabla"] # 0xf421 - - "𝛁": [t: "pogrubiona nabla"] # 0x1d6c1 + - "": [T: "pogrubiona nabla"] # 0xf421 + - "𝛁": [T: "pogrubiona nabla"] # 0x1d6c1 - "𝛛𝛜𝛝𝛞𝛟𝛠𝛡": # 0x1D6DB - 0x1D6E1 - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '𝛛𝛜𝛝𝛞𝛟𝛠𝛡', '∂εθκφρπ')" - "": # 0xF43C - 0xF441 - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '', '∂εθκφρπ')" - "𝛢-𝛺": # 0x1d6e2 - 0x1d6fa - # - t: "kursywa" + # - T: "kursywa" - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹𝛺', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" - "-": # 0xf442 - 0xf45a - # - t: "kursywa" + # - T: "kursywa" - spell: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" - "𝛼-𝜔": # 0x1d6fc - 0x1d714 - # - t: "kursywa" + # - T: "kursywa" - spell: "translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌𝜍𝜎𝜏𝜐𝜑𝜒𝜓𝜔', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "-": # 0xf45c - 0xf474 - # - t: "kursywa" + # - T: "kursywa" - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" - - "": [t: "kursywa nabla"] # 0xf45b - - "𝛻": [t: "kursywa nabla"] # 0x1d6fb + - "": [T: "kursywa nabla"] # 0xf45b + - "𝛻": [T: "kursywa nabla"] # 0x1d6fb - "𝜕𝜖𝜗𝜘𝜙𝜚𝜛": # 0x1d715 - 0x1d71b - # - t: "kursywa" + # - T: "kursywa" - spell: "translate('.', '𝜕𝜖𝜗𝜘𝜙𝜚𝜛', '∂εθκφρπ')" - "": # 0xf475 - 0xf47b - # - t: "kursywa" + # - T: "kursywa" - spell: "translate('.', '', '∂εθκφρπ')" - "𝜜-𝜴": # 0x1d71c - 0x1d734 - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳𝜴', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" - "-": # 0xf47c - 0xf494 - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" - "𝜶-𝝎": # 0x1d736 - 0x1d74e - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆𝝇𝝈𝝉𝝊𝝋𝝌𝝍𝝎', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "-": # 0xf496 - 0xf4ae - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝝏𝝐𝝑𝝒𝝓𝝔𝝕": # 0x1d74f - 0x1d755 - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '𝝏𝝐𝝑𝝒𝝓𝝔𝝕', '∂εθκφρπ')" - "": # 0xf422 - 0xf43a - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '', '∂εθκφρπ')" - - "𝜵": [t: "pogrubiona pochylona nabla"] # 0x1d735 - - "": [t: "pogrubiona pochylona nabla"] # 0xf495 + - "𝜵": [T: "pogrubiona pochylona nabla"] # 0x1d735 + - "": [T: "pogrubiona pochylona nabla"] # 0xf495 - "𝝖-𝝮": # 0x1d756 - 0x1d76e - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭𝝮', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" - "-": # 0xf4b6 - 0xf4ce - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" - "𝝰-𝞈": # 0x1d770 - 0x1d788 - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿𝞀𝞁𝞂𝞃𝞄𝞅𝞆𝞇𝞈', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "-": # 0xf4d0 - 0xf4e8 - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝞉𝞊𝞋𝞌𝞍𝞎𝞏": # 0x1d789 - 0x1d78f - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '𝞉𝞊𝞋𝞌𝞍𝞎𝞏', '∂εθκφρπ')" - "": # 0xf4e9 - 0xf4ef - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '', '∂εθκφρπ')" - - "": [t: "pogrubiona nabla"] # 0xf4cf - - "𝝯": [t: "pogrubiona nabla"] # 0x1d76f + - "": [T: "pogrubiona nabla"] # 0xf4cf + - "𝝯": [T: "pogrubiona nabla"] # 0x1d76f - "𝞐-𝞨": # 0x1d790 - 0x1d7a8 - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" - "-": # 0xf4f0 - 0xf508 - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" - "𝞪-𝟂": # 0x1d7aa - 0x1d7c2 - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺𝞻𝞼𝞽𝞾𝞿𝟀𝟁𝟂', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "-": # 0xf50a - 0xf522 - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" - "𝟃𝟄𝟅𝟆𝟇𝟈𝟉": # 0x1d7c3 - 0x1d7c9 - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '𝟃𝟄𝟅𝟆𝟇𝟈𝟉', '∂εθκφρπ')" - "": # 0xf523 - 0xf529 - # - t: "pogrubiona kursywa" - - t: "pogrubione" + # - T: "pogrubiona kursywa" + - T: "pogrubione" - spell: "translate('.', '', '∂εθκφρπ')" - - "": [t: "pogrubiona nabla"] # 0xf509 - - "𝞩": [t: "pogrubiona nabla"] # 0x1d7a9 + - "": [T: "pogrubiona nabla"] # 0xf509 + - "𝞩": [T: "pogrubiona nabla"] # 0x1d7a9 - "-": # 0xf52e - 0xf537 (old MathType) - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '', '0123456789')" - "𝟎-𝟗": # 0x1d7ce - 0x1d7d7 - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789')" - "-": # 0xf52e - 0xf537 (old MathType) - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '', '0123456789')" - "𝟬-𝟵": # 0x1D7EC - 0x1D7F5 - - t: "pogrubione" + - T: "pogrubione" - spell: "translate('.', '𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵', '0123456789')" - "-": # 0xf556 - 0xf55f (old MathType) @@ -3532,37 +3532,37 @@ - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" - - "": [t: "nieznany znak"] # 0xf700 - - "": [t: "dolny prawy i dolny lewy trójkąt"] # 0xf726 - - "": [t: "poziome przedłużenie wielokropka"] # 0xf72d - - "": [t: "poziome przedłużenie wielokropka w linii środkowej"] # 0xf72e - - "": [t: "przedłużenie znaku pierwiastka"] # 0xf8e5 - - "": [t: "pionowy strzałka przedłużenie"] # 0xf8e6 - - "": [t: "poziomy strzałka przedłużenie"] # 0xf8e7 - - "": [t: "bezszeryfowy znak zastrzeżony"] # 0xf8e8 - - "": [t: "znak praw autorskich znak bezszeryfowe"] # 0xf8e9 - - "": [t: "bezszeryfowy znak towarowy"] # 0xf8ea - - "": [t: "lewy nawias górny"] # 0xf8eb - - "": [t: "lewy nawias przedłużenie"] # 0xf8ec - - "": [t: "lewy nawias dolny"] # 0xf8ed - - "": [t: "lewy nawias kwadratowy górny"] # 0xf8ee - - "": [t: "lewy nawias kwadratowy przedłużenie"] # 0xf8ef - - "": [t: "lewy nawias kwadratowy dolny"] # 0xf8f0 - - "": [t: "lewy nawias klamrowy górny"] # 0xf8f1 - - "": [t: "lewy nawias klamrowy środkowy"] # 0xf8f2 - - "": [t: "lewy nawias klamrowy dolny"] # 0xf8f3 - - "": [t: "nawias klamrowy przedłużenie"] # 0xf8f4 - - "": [t: "całka przedłużenie"] # 0xf8f5 - - "": [t: "prawy nawias górny"] # 0xf8f6 - - "": [t: "prawy nawias przedłużenie"] # 0xf8f7 - - "": [t: "prawy nawias dolny"] # 0xf8f8 - - "": [t: "prawy nawias kwadratowy górny"] # 0xf8f9 - - "": [t: "prawy nawias kwadratowy przedłużenie"] # 0xf8fa - - "": [t: "prawy nawias kwadratowy dolny"] # 0xf8fb - - "": [t: "prawy nawias klamrowy górny"] # 0xf8fc - - "": [t: "prawy nawias klamrowy środkowy"] # 0xf8fd - - "": [t: "prawy nawias klamrowy dolny"] # 0xf8fe - - "": [t: "logo jabłka"] # 0xf8ff + - "": [T: "nieznany znak"] # 0xf700 + - "": [T: "dolny prawy i dolny lewy trójkąt"] # 0xf726 + - "": [T: "poziome przedłużenie wielokropka"] # 0xf72d + - "": [T: "poziome przedłużenie wielokropka w linii środkowej"] # 0xf72e + - "": [T: "przedłużenie znaku pierwiastka"] # 0xf8e5 + - "": [T: "pionowy strzałka przedłużenie"] # 0xf8e6 + - "": [T: "poziomy strzałka przedłużenie"] # 0xf8e7 + - "": [T: "bezszeryfowy znak zastrzeżony"] # 0xf8e8 + - "": [T: "znak praw autorskich znak bezszeryfowe"] # 0xf8e9 + - "": [T: "bezszeryfowy znak towarowy"] # 0xf8ea + - "": [T: "lewy nawias górny"] # 0xf8eb + - "": [T: "lewy nawias przedłużenie"] # 0xf8ec + - "": [T: "lewy nawias dolny"] # 0xf8ed + - "": [T: "lewy nawias kwadratowy górny"] # 0xf8ee + - "": [T: "lewy nawias kwadratowy przedłużenie"] # 0xf8ef + - "": [T: "lewy nawias kwadratowy dolny"] # 0xf8f0 + - "": [T: "lewy nawias klamrowy górny"] # 0xf8f1 + - "": [T: "lewy nawias klamrowy środkowy"] # 0xf8f2 + - "": [T: "lewy nawias klamrowy dolny"] # 0xf8f3 + - "": [T: "nawias klamrowy przedłużenie"] # 0xf8f4 + - "": [T: "całka przedłużenie"] # 0xf8f5 + - "": [T: "prawy nawias górny"] # 0xf8f6 + - "": [T: "prawy nawias przedłużenie"] # 0xf8f7 + - "": [T: "prawy nawias dolny"] # 0xf8f8 + - "": [T: "prawy nawias kwadratowy górny"] # 0xf8f9 + - "": [T: "prawy nawias kwadratowy przedłużenie"] # 0xf8fa + - "": [T: "prawy nawias kwadratowy dolny"] # 0xf8fb + - "": [T: "prawy nawias klamrowy górny"] # 0xf8fc + - "": [T: "prawy nawias klamrowy środkowy"] # 0xf8fd + - "": [T: "prawy nawias klamrowy dolny"] # 0xf8fe + - "": [T: "logo jabłka"] # 0xf8ff - "ff": [T: "ff"] # 0xfb00 - "fi": [t: "fi"] # 0xfb01 - "fl": [T: "fl"] # 0xfb02 @@ -3570,31 +3570,31 @@ - "ffl": [T: "ffl"] # 0xfb04 - "ſt": [T: "ft"] # 0xfb05 - "st": [T: "st"] # 0xfb06 - - "﬩": [t: "hebrajska litera alternatywny znak plus"] # 0xfb29 - - "︠": [t: "lewa połowa ligatury, ozdobnik"] # 0xfe20 - - "︡": [t: "prawa połowa ligatury, ozdobnik"] # 0xfe21 - - "︢": [t: "podwójny tylda lewa połowa ozdobnik"] # 0xfe22 - - "︣": [t: "podwójny tylda prawa połowa ozdobnik"] # 0xfe23 - - "︤": [t: "makron lewa połowa ozdobnik"] # 0xfe24 - - "︥": [t: "makron prawa połowa ozdobnik"] # 0xfe25 - - "︦": [t: "łączący ozdobnik makronu"] # 0xfe26 - - "︵": [t: "przez nawias"] # 0xfe35 - - "︶": [t: "pod nawias"] # 0xfe36 - - "︷": [t: "przez nawias klamrowy"] # 0xfe37 - - "︸": [t: "pod nawias klamrowy"] # 0xfe38 - - "︿": [t: "przez kąt nawias kwadratowy"] # 0xfe3f - - "﹀": [t: "pod kąt nawias kwadratowy"] # 0xfe40 - - "﹡": [t: "mały gwiazdka"] # 0xfe61 - - "﹢": [t: "mały plus"] # 0xfe62 - - "﹣": [t: "mały minus"] # 0xfe63 - - "﹤": [t: "mały mniejsze niż"] # 0xfe64 - - "﹥": [t: "mały większe niż"] # 0xfe65 - - "﹦": [t: "mały równa się"] # 0xfe66 - - "+": [t: "pełnoszeroki znak plus"] # 0xff0b + - "﬩": [T: "hebrajska litera alternatywny znak plus"] # 0xfb29 + - "︠": [T: "lewa połowa ligatury, ozdobnik"] # 0xfe20 + - "︡": [T: "prawa połowa ligatury, ozdobnik"] # 0xfe21 + - "︢": [T: "podwójny tylda lewa połowa ozdobnik"] # 0xfe22 + - "︣": [T: "podwójny tylda prawa połowa ozdobnik"] # 0xfe23 + - "︤": [T: "makron lewa połowa ozdobnik"] # 0xfe24 + - "︥": [T: "makron prawa połowa ozdobnik"] # 0xfe25 + - "︦": [T: "łączący ozdobnik makronu"] # 0xfe26 + - "︵": [T: "przez nawias"] # 0xfe35 + - "︶": [T: "pod nawias"] # 0xfe36 + - "︷": [T: "przez nawias klamrowy"] # 0xfe37 + - "︸": [T: "pod nawias klamrowy"] # 0xfe38 + - "︿": [T: "przez kąt nawias kwadratowy"] # 0xfe3f + - "﹀": [T: "pod kąt nawias kwadratowy"] # 0xfe40 + - "﹡": [T: "mały gwiazdka"] # 0xfe61 + - "﹢": [T: "mały plus"] # 0xfe62 + - "﹣": [T: "mały minus"] # 0xfe63 + - "﹤": [T: "mały mniejsze niż"] # 0xfe64 + - "﹥": [T: "mały większe niż"] # 0xfe65 + - "﹦": [T: "mały równa się"] # 0xfe66 + - "+": [T: "pełnoszeroki znak plus"] # 0xff0b - "<": [T: "mniejsze niż"] # 0xff1c - "=": [T: "równa się"] # 0xff1d - ">": [T: "większe niż"] # 0xff1e - - "\": [t: "ukośnik odwrotny"] # 0xff3c + - "\": [T: "ukośnik odwrotny"] # 0xff3c - "^": [T: "daszek"] # 0xff3e - "|": # 0xff5c # note: for ClearSpeak and SimpleSpeak, "|" inside of sets is handled at the mrow level, same for 'sets' @@ -3616,21 +3616,21 @@ - else: [T: "dzieli"] - "~": [T: "tylda"] # 0xff5e - "¬": [T: "nie"] # 0xffe2 - - "←": [t: "strzałka w lewo"] # 0xffe9 - - "↑": [t: "strzałka w górę"] # 0xffea - - "→": [t: "strzałka w prawo"] # 0xffeb - - "↓": [t: "strzałka w dół"] # 0xffec - - "": [t: "nieznany lub brakujący obiekt"] # 0xfffc - - "�": [t: "nieznany lub brakujący znak"] # 0xfffd + - "←": [T: "strzałka w lewo"] # 0xffe9 + - "↑": [T: "strzałka w górę"] # 0xffea + - "→": [T: "strzałka w prawo"] # 0xffeb + - "↓": [T: "strzałka w dół"] # 0xffec + - "": [T: "nieznany lub brakujący obiekt"] # 0xfffc + - "�": [T: "nieznany lub brakujący znak"] # 0xfffd - - "🣑": [t: "jest w równowadze z"] # 0x1F8D1 - - "🣒": [t: "jest w równowadze przesuniętej w prawo z"] # 0x1F8D2 - - "🣓": [t: "jest w równowadze przesuniętej w lewo z"] # 0x1F8D3 + - "🣑": [T: "jest w równowadze z"] # 0x1F8D1 + - "🣒": [T: "jest w równowadze przesuniętej w prawo z"] # 0x1F8D2 + - "🣓": [T: "jest w równowadze przesuniętej w lewo z"] # 0x1F8D3 # MathJax v4 has adopted these PUA values for some partial chem bonds that aren't in Unicode - - "\uE410": [t: "wiązanie cząstkowe"] # 0xe410 - - "\uE411": [t: "podwójne wiązanie cząstkowe"] # 0xe411 - - "\uE412": [t: "potrójne wiązanie cząstkowe"] # 0xe412 + - "\uE410": [T: "wiązanie cząstkowe"] # 0xe410 + - "\uE411": [T: "podwójne wiązanie cząstkowe"] # 0xe411 + - "\uE412": [T: "potrójne wiązanie cząstkowe"] # 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 diff --git a/Rules/Languages/pl/unicode.yaml b/Rules/Languages/pl/unicode.yaml index bd3ee353a..c67b5cc8a 100644 --- a/Rules/Languages/pl/unicode.yaml +++ b/Rules/Languages/pl/unicode.yaml @@ -5,12 +5,12 @@ - "a": - test: if: "$TTS='none'" - then: [t: "a"] # long "a" sound in all speech engines I tested (espeak, MS SAPI, eloquence, + 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: "."] + then: [T: "."] else: [spell: "'.'"] # Capital letters are a little tricky: users can pick their favorite word (something that was requested) and @@ -30,14 +30,14 @@ if: "$SpeechOverrides_CapitalLetters = ''" then_test: if: "$Impairment = 'Blindness'" - then: [t: "wielka"] + then: [T: "wielka"] else: [x: "$SpeechOverrides_CapitalLetters"] - pitch: value: "$CapitalLetters_Pitch" replace: - test: if: "$TTS='none'" - then: [t: "a"] + then: [T: "a"] else: [spell: "'a'"] - "B-Z": @@ -53,7 +53,7 @@ if: "$SpeechOverrides_CapitalLetters = ''" then_test: if: "$Impairment = 'Blindness'" - then: [t: "wielka"] + then: [T: "wielka"] else: [x: "$SpeechOverrides_CapitalLetters"] - pitch: value: "$CapitalLetters_Pitch" @@ -73,12 +73,12 @@ else: [T: "wykrzyknik"] # 0x21 else: [T: "silnia"] # 0x21 - - "\"": [t: "cudzysłów"] # 0x22 - - "#": [t: "numer"] # 0x23 - - "$": [t: "dolary"] # 0x24 + - "\"": [T: "cudzysłów"] # 0x22 + - "#": [T: "numer"] # 0x23 + - "$": [T: "dolary"] # 0x24 - "%": [T: "procent"] # 0x25 - - "&": [t: "znak et"] # 0x26 - - "'": [t: "apostrof"] # 0x27 + - "&": [T: "znak et"] # 0x26 + - "'": [T: "apostrof"] # 0x27 - "(": # 0x28 - test: if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' @@ -105,7 +105,7 @@ then: [T: "gwiazdkowa"] # 0x2a (norma 'gwiazdkowa' - * jako indeks normy) else_test: if: "parent::*[name(.)='msup' or name(.)='msubsup' or name(.)='skip-super']" - then: [t: "gwiazda"] # 0x2a + then: [T: "gwiazda"] # 0x2a else: [T: "razy"] # 0x2a - "+": [T: "plus"] # 0x2b - ",": # 0x2c @@ -157,7 +157,7 @@ then: [T: "jest"] - T: "większe niż" - "?": [T: "znak zapytania"] # 0x3f - - "@": [t: "małpa"] # 0x40 + - "@": [T: "małpa"] # 0x40 - "[": # 0x5b - test: if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' @@ -173,9 +173,9 @@ - test: if: "parent::m:modified-variable or parent::m:mover" then: [T: "daszek"] - else: [t: "daszek"] - - "_": [t: "pod kreska"] # 0x5f - - "`": [t: "grawis"] # 0x60 + else: [T: "daszek"] + - "_": [T: "pod kreska"] # 0x5f + - "`": [T: "grawis"] # 0x60 - "{": # 0x7b - test: if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' @@ -226,7 +226,7 @@ then: [T: "stopni"] else: [T: "stopień"] - "±": [T: "plus minus"] # 0xb1 - - "´": [t: "akut"] # 0xb4 + - "´": [T: "akut"] # 0xb4 - "·": # 0xB7 - test: if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')] or not($SpeechStyle = 'ClearSpeak' and $ClearSpeak_MultSymbolDot = 'Auto')" @@ -240,21 +240,21 @@ then: [T: "razy"] - else_if: "$ClearSpeak_MultSymbolX = 'By'" then: [T: "na"] - else: [t: "krzyż"] + else: [T: "krzyż"] else_test: if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" - then: [t: "krzyż"] + then: [T: "krzyż"] else: [T: "razy"] - "÷": [T: "podzielone przez"] # 0xf7 - - "̀": [t: "ozdobnik akcentu grawis"] # 0x300 - - "́": [t: "ozdobnik akcentu ostrego"] # 0x301 - - "̂": [t: "ozdobnik daszka"] # 0x302 - - "̃": [t: "ozdobnik tyldy"] # 0x303 - - "̄": [t: "ozdobnik makronu"] # 0x304 - - "̅": [t: "ozdobnik kreski górnej"] # 0x305 - - "̆": [t: "znak krótkości"] # 0x306 - - "̇": [t: "ozdobnik kropki nad"] # 0x307 + - "̀": [T: "ozdobnik akcentu grawis"] # 0x300 + - "́": [T: "ozdobnik akcentu ostrego"] # 0x301 + - "̂": [T: "ozdobnik daszka"] # 0x302 + - "̃": [T: "ozdobnik tyldy"] # 0x303 + - "̄": [T: "ozdobnik makronu"] # 0x304 + - "̅": [T: "ozdobnik kreski górnej"] # 0x305 + - "̆": [T: "znak krótkości"] # 0x306 + - "̇": [T: "ozdobnik kropki nad"] # 0x307 # Note: ClearSpeak has pref TriangleSymbol for "Δ", but that is wrong - "Α-Ω": @@ -270,47 +270,47 @@ if: "$SpeechOverrides_CapitalLetters = ''" then_test: if: "$Impairment = 'Blindness'" - then: [t: "wielka"] + then: [T: "wielka"] 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: "alfa"] # 0x3b1 + - "α": [T: "alfa"] # 0x3b1 - "β": [t: "beta"] # 0x3b2 - "γ": [t: "gamma"] # 0x3b3 - "δ": [t: "delta"] # 0x3b4 - "ε": [t: "epsilon"] # 0x3b5 - - "ζ": [t: "dzeta"] # 0x3b6 + - "ζ": [T: "dzeta"] # 0x3b6 - "η": [t: "eta"] # 0x3b7 - "θ": [t: "theta"] # 0x3b8 - - "ι": [t: "jota"] # 0x3b9 + - "ι": [T: "jota"] # 0x3b9 - "κ": [t: "kappa"] # 0x3ba - "λ": [t: "lambda"] # 0x3bb - - "μ": [t: "mi"] # 0x3bc - - "ν": [t: "ni"] # 0x3bd - - "ξ": [t: "ksi"] # 0x3be - - "ο": [t: "omikron"] # 0x3bf + - "μ": [T: "mi"] # 0x3bc + - "ν": [T: "ni"] # 0x3bd + - "ξ": [T: "ksi"] # 0x3be + - "ο": [T: "omikron"] # 0x3bf - "π": [t: "pi"] # 0x3c0 - - "ρ": [t: "ro"] # 0x3c1 - - "ς": [t: "sigma końcowa"] # 0x3c2 + - "ρ": [T: "ro"] # 0x3c1 + - "ς": [T: "sigma końcowa"] # 0x3c2 - "σ": [t: "sigma"] # 0x3c3 - "τ": [t: "tau"] # 0x3c4 - - "υ": [t: "ypsilon"] # 0x3c5 - - "φ": [t: "fi"] # 0x3c6 + - "υ": [T: "ypsilon"] # 0x3c5 + - "φ": [T: "fi"] # 0x3c6 - "χ": [t: "chi"] # 0x3c7 - "ψ": [t: "psi"] # 0x3c8 - "ω": [t: "omega"] # 0x3c9 - - "ϕ": [t: "fi"] # 0x3d5 + - "ϕ": [T: "fi"] # 0x3d5 - "ϖ": [t: "pi"] # 0x3d6 - "ϵ": [t: "epsilon"] # 0x3f5 - - "϶": [t: "odwrócony epsilon"] # 0x3f6 + - "϶": [T: "odwrócony epsilon"] # 0x3f6 - - "–": [t: "półpauza"] # 0x2013 - - "—": [t: "pauza"] # 0x2014 - - "―": [t: "kreska pozioma"] # 0x2015 - - "‖": [t: "podwójna pionowa kreska"] # 0x2016 + - "–": [T: "półpauza"] # 0x2013 + - "—": [T: "pauza"] # 0x2014 + - "―": [T: "kreska pozioma"] # 0x2015 + - "‖": [T: "podwójna pionowa kreska"] # 0x2016 - "…": # 0x2026 test: if: @@ -318,11 +318,11 @@ # must be ClearSpeak and $ClearSpeak_Ellipses = 'AndSoOn' # speak '…' as 'and so on...' unless expr starts with '…' - "../*[1][.='…']" - then: [t: "trzy kropki"] + then: [T: "trzy kropki"] else_test: # must have $ClearSpeak_Ellipses = 'AndSoOn' if: "count(following-sibling::*) = 0" - then: [t: "i tak dalej"] - else: [t: "i tak dalej aż do"] + then: [T: "i tak dalej"] + else: [T: "i tak dalej aż do"] - "⁡": # 0x2061 - test: @@ -340,55 +340,55 @@ - "⁢": [T: ""] # 0x2062 - "⁣": [T: ""] # 0x2063 - "⁤": [T: "i"] # 0x2064 - - "′": [t: "prim"] # 0x2032 - - "″": [t: "podwójny prim"] # 0x2033 - - "‴": [t: "potrójny prim"] # 0x2034 + - "′": [T: "prim"] # 0x2032 + - "″": [T: "podwójny prim"] # 0x2033 + - "‴": [T: "potrójny prim"] # 0x2034 - "ℂℕℚℝℤ": # here we rely on this running through the table again to speak "cap xxx" - - t: "dwukreskowe" + - T: "dwukreskowe" - spell: "translate('.', 'ℂℕℚℝℤ', 'CNQRZ')" - - "℃": [t: "stopnie Celsjusza"] # 0x2103 - - "℉": [t: "stopnie Fahrenheita"] # 0x2109 + - "℃": [T: "stopnie Celsjusza"] # 0x2103 + - "℉": [T: "stopnie Fahrenheita"] # 0x2109 - "ℋℛℓ": # 0x210b - - t: "pisane" + - T: "pisane" - spell: "translate('.', 'ℋℛℓ', 'HRl')" - - "ℎ": [t: "stała Plancka"] # 0x210e + - "ℎ": [T: "stała Plancka"] # 0x210e - "ℜ": # 0x211c - - t: "fraktura" + - T: "fraktura" - spell: "'R'" - - "Ω": [t: "omy"] # 0x2126 - - "K": [t: "kelwin"] # 0x212a - - "Å": [t: "angstremy"] # 0x212b + - "Ω": [T: "omy"] # 0x2126 + - "K": [T: "kelwin"] # 0x212a + - "Å": [T: "angstremy"] # 0x212b - "ⅆⅇⅈⅉ": # 0x2146-9 - - t: "dwukreskowa kursywa" + - T: "dwukreskowa kursywa" - spell: "translate('.', 'ⅆⅇⅈⅉ', 'deij')" - - "←": [t: "strzałka w lewo"] # 0x2190 - - "↑": [t: "strzałka w górę"] # 0x2191 + - "←": [T: "strzałka w lewo"] # 0x2190 + - "↑": [T: "strzałka w górę"] # 0x2191 - "→": # 0x2192 - test: if: "ancestor::*[2][self::m:limit or self::m:limit-sup or self::m:limit-inf]" - then: [t: "dąży do"] + then: [T: "dąży do"] else_test: if: "parent::*[self::m:mrow]/parent::*[self::m:subscripted-norm]" - then: [t: "do"] # indeks normy operatorowej ‖·‖_{p→q}: → czyta sie "do" - else: [t: "strzałka w prawo"] + then: [T: "do"] # indeks normy operatorowej ‖·‖_{p→q}: → czyta sie "do" + else: [T: "strzałka w prawo"] - - "↓": [t: "strzałka w dół"] # 0x2193 + - "↓": [T: "strzałka w dół"] # 0x2193 - "⇒": [T: "implikuje"] # 0x21d2 - - "∀": [t: "dla każdego"] # 0x2200 + - "∀": [T: "dla każdego"] # 0x2200 - "∂": # 0x2202 - test: if: "$Verbosity='Terse'" - then: [t: "cząstkowe"] - else: [t: "pochodna cząstkowa"] - - "∃": [t: "istnieje"] # 0x2203 - - "∄": [t: "nie istnieje"] # 0x2204 + then: [T: "cząstkowe"] + else: [T: "pochodna cząstkowa"] + - "∃": [T: "istnieje"] # 0x2203 + - "∄": [T: "nie istnieje"] # 0x2204 - "∅": [T: "zbiór pusty"] # 0x2205 - "∆": # 0x2206 - - t: "laplasjan" + - T: "laplasjan" - "∈": # 0x2208 - test: if: "$SpeechStyle != 'ClearSpeak'" @@ -396,7 +396,7 @@ - test: if: "$Verbosity!='Terse' and not(ancestor::*[self::m:set])" # "the set x is an element of ..." sounds bad" then: [T: "jest"] - - t: "elementem zbioru" + - T: "elementem zbioru" # 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 @@ -404,43 +404,43 @@ - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'In' then: [T: "do"] - else_if: $ClearSpeak_SetMemberSymbol = 'Member' - then: [t: "element zbioru"] + then: [T: "element zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'Element' - then: [t: "należy do"] - - else: [t: "należące do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + then: [T: "należy do"] + - else: [T: "należące do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' else_test: - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' - then: [t: "jest elementem zbioru"] + then: [T: "jest elementem zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'Element' - then: [t: "jest elementem zbioru"] + then: [T: "jest elementem zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'In' - then: [t: "należy do"] - - else: [t: "należy do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + then: [T: "należy do"] + - else: [T: "należy do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' - "∉": # 0x2209 # rule is identical to 0x2208 - test: if: "$SpeechStyle != 'ClearSpeak'" then: - - t: "nie jest elementem zbioru" + - T: "nie jest elementem zbioru" # 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: "nie należy do"] + then: [T: "nie należy do"] - else_if: $ClearSpeak_SetMemberSymbol = 'Member' - then: [t: "nie jest elementem zbioru"] + then: [T: "nie jest elementem zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'Element' - then: [t: "nie jest elementem zbioru"] - - else: [t: "nienależące do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + then: [T: "nie jest elementem zbioru"] + - else: [T: "nienależące do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' else_test: - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' - then: [t: "nie jest elementem zbioru"] + then: [T: "nie jest elementem zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'Element' - then: [t: "nie jest elementem zbioru"] + then: [T: "nie jest elementem zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'In' - then: [t: "nie należy do"] - - else: [t: "nie należy do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + then: [T: "nie należy do"] + - else: [T: "nie należy do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' - "∊": # 0x220a - test: if: "$SpeechStyle != 'ClearSpeak'" @@ -448,7 +448,7 @@ - test: if: "$Verbosity!='Terse' and not(ancestor::*[self::m:set])" # "the set x is an element of ..." sounds bad" then: [T: "jest"] - - t: "element zbioru" + - T: "element zbioru" # 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 @@ -456,20 +456,20 @@ - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'In' then: [T: "do"] - else_if: $ClearSpeak_SetMemberSymbol = 'Member' - then: [t: "element zbioru"] + then: [T: "element zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'Element' - then: [t: "należy do"] - - else: [t: "należące do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + then: [T: "należy do"] + - else: [T: "należące do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' else_test: - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' - then: [t: "jest elementem zbioru"] + then: [T: "jest elementem zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'Element' - then: [t: "jest elementem zbioru"] + then: [T: "jest elementem zbioru"] - else_if: $ClearSpeak_SetMemberSymbol = 'In' - then: [t: "należy do"] - - else: [t: "należy do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + then: [T: "należy do"] + - else: [T: "należy do"] # $ClearSpeak_SetMemberSymbol = 'Belongs' - "∏": [T: "iloczyn"] # 0x220f - - "∐": [t: "koprodukt"] # 0x2210 + - "∐": [T: "koprodukt"] # 0x2210 - "∑": [T: "suma"] # 0x2211 - "−": [T: "minus"] # 0x2212 - "∓": [T: "minus plus"] # 0x2213 @@ -481,60 +481,60 @@ - "parent::*[name(.)='msubsup' and *[1][self::m:mo and .='∥']] and count(preceding-sibling::*)=1" then: [T: "gwiazdkowa"] # norma ‖·‖_* -> "norma gwiazdkowa" else: [T: "razy"] # 0x2217 - - "∘": [t: "złożone z"] # 0x2218 + - "∘": [T: "złożone z"] # 0x2218 - "√": # 0x221a - - t: "pierwiastek kwadratowy z" + - T: "pierwiastek kwadratowy z" - "∝": # 0x221d - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "proporcjonalne do" + - T: "proporcjonalne do" - "∞": - test: if: "ancestor::*[self::m:limit or self::m:limit-sup or self::m:limit-inf or self::m:large-op] or parent::*[self::m:subscripted-norm] or parent::*[self::m:mrow]/parent::*[self::m:subscripted-norm]" then: [T: "nieskończoności"] else: [T: "nieskończoność"] # 0x221e - - "∟": [t: "kąt prosty"] # 0x221f - - "∠": [t: "kąt"] # 0x2220 - - "∡": [t: "kąt mierzony"] # 0x2221 + - "∟": [T: "kąt prosty"] # 0x221f + - "∠": [T: "kąt"] # 0x2220 + - "∡": [T: "kąt mierzony"] # 0x2221 - "∣": [T: "dzieli"] # 0x2223 - - "∤": [t: "nie dzieli"] # 0x2224 + - "∤": [T: "nie dzieli"] # 0x2224 - "∥": # 0x2225 - test: if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" - then: [t: "podwójna pionowa kreska"] + then: [T: "podwójna pionowa kreska"] else: - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "równoległe do" + - T: "równoległe do" - "∦": # 0x2226 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "nierównoległe do" + - T: "nierównoległe do" - "∧": [T: "i"] # 0x2227 - "∨": [T: "lub"] # 0x2228 - "∩": [T: "przecięcie"] # 0x2229 - "∪": [T: "suma zbiorów"] # 0x222a - "∫": [T: "całka"] # 0x222b - - "∬": [t: "całka podwójna"] # 0x222c - - "∭": [t: "całka potrójna"] # 0x222d - - "∮": [t: "całka krzywoliniowa"] # 0x222e + - "∬": [T: "całka podwójna"] # 0x222c + - "∭": [T: "całka potrójna"] # 0x222d + - "∮": [T: "całka krzywoliniowa"] # 0x222e - "∶": # 0x2236 - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - T: "do" - - "∷": [t: "jako"] # 0x2237 - - "∼": [t: "tylda"] # 0x223c - - "∽": [t: "odwrócona tylda"] # 0x223d + - "∷": [T: "jako"] # 0x2237 + - "∼": [T: "tylda"] # 0x223c + - "∽": [T: "odwrócona tylda"] # 0x223d - "∾": # 0x223e - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "most dodatnie" - - "∿": [t: "fala sinusoidalna"] # 0x223f + - T: "most dodatnie" + - "∿": [T: "fala sinusoidalna"] # 0x223f - "≠": # 0x2260 - test: if: "$Verbosity!='Terse'" @@ -544,7 +544,7 @@ - test: if: "$Verbosity!='Terse'" then: [T: "jest"] - - t: "identyczne z" + - T: "identyczne z" - "≤": # 0x2264 - test: if: "$Verbosity!='Terse'" @@ -555,31 +555,31 @@ if: "$Verbosity!='Terse'" then: [T: "jest"] - T: "większe lub równe" - - "≦": [t: "mniejsze nad równe"] # 0x2266 - - "≧": [t: "większe nad równe"] # 0x2267 - - "≺": [t: "poprzedza"] # 0x227a - - "≻": [t: "następuje po"] # 0x227b + - "≦": [T: "mniejsze nad równe"] # 0x2266 + - "≧": [T: "większe nad równe"] # 0x2267 + - "≺": [T: "poprzedza"] # 0x227a + - "≻": [T: "następuje po"] # 0x227b - "⊂": # 0x2282 - test: if: "$Verbosity!='Terse'" - then: [t: "jest"] - - t: "podzbiorem" + then: [T: "jest"] + - T: "podzbiorem" - "⊃": # 0x2283 - test: if: "$Verbosity!='Terse'" - then: [t: "jest"] - - t: "nadzbiorem" + then: [T: "jest"] + - T: "nadzbiorem" - "⊄": # 0x2284 - - t: "nie jest podzbiorem" + - T: "nie jest podzbiorem" - "⊅": # 0x2285 - - t: "nie jest nadzbiorem" + - T: "nie jest nadzbiorem" - "⊆": # 0x2286 - test: if: "$Verbosity!='Terse'" - then: [t: "jest"] - - t: "podzbiorem lub równym" + then: [T: "jest"] + - T: "podzbiorem lub równym" - "⊇": # 0x2287 - test: if: "$Verbosity!='Terse'" - then: [t: "jest"] - - t: "nadzbiorem lub równym" + then: [T: "jest"] + - T: "nadzbiorem lub równym" diff --git a/tests/Languages/pl.rs b/tests/Languages/pl.rs index 44016ff5b..c9423ea9f 100644 --- a/tests/Languages/pl.rs +++ b/tests/Languages/pl.rs @@ -2,6 +2,8 @@ // Existing hand-written Polish tests (norms, degrees, relations, etc.) mod pl; +mod number_sets; +mod unit_plurals; // Tests mirrored from the English suite, with expected speech localized to Polish. mod ClearSpeak { diff --git a/tests/Languages/pl/ClearSpeak/mfrac.rs b/tests/Languages/pl/ClearSpeak/mfrac.rs index f7197bcb2..a53bba559 100644 --- a/tests/Languages/pl/ClearSpeak/mfrac.rs +++ b/tests/Languages/pl/ClearSpeak/mfrac.rs @@ -86,7 +86,7 @@ fn frac_with_units() -> Result<()> { "; - test("pl", "ClearSpeak", expr, "62 milas na godzina")?; + test("pl", "ClearSpeak", expr, "62 mile na godzina")?; return Ok(()); } diff --git a/tests/Languages/pl/SimpleSpeak/mfrac.rs b/tests/Languages/pl/SimpleSpeak/mfrac.rs index 2e84dc987..a07f0592b 100644 --- a/tests/Languages/pl/SimpleSpeak/mfrac.rs +++ b/tests/Languages/pl/SimpleSpeak/mfrac.rs @@ -164,7 +164,7 @@ fn frac_with_units() -> Result<()> { "; - test("pl", "SimpleSpeak", expr, "62 milas na godzina")?; + test("pl", "SimpleSpeak", expr, "62 mile na godzina")?; return Ok(()); } @@ -198,7 +198,7 @@ fn number_in_numerator_with_units() -> Result<()> { mi "; - test("pl", "SimpleSpeak", expr, "3 galons na mila")?; + test("pl", "SimpleSpeak", expr, "3 galony na mila")?; return Ok(()); } @@ -212,7 +212,7 @@ fn units_with_powers() -> Result<()> { s2 "; - test("pl", "SimpleSpeak", expr, "3 metrs na sekunda do kwadratu")?; + test("pl", "SimpleSpeak", expr, "3 metry na sekunda do kwadratu")?; return Ok(()); } diff --git a/tests/Languages/pl/number_sets.rs b/tests/Languages/pl/number_sets.rs new file mode 100644 index 000000000..6d672447b --- /dev/null +++ b/tests/Languages/pl/number_sets.rs @@ -0,0 +1,69 @@ +/// Number sets: ℂ ℕ ℚ ℝ ℤ, on their own and with a dimension (ℝ³). +/// +/// These rules had no Polish test at all, which is how the earlier defects in +/// this file were able to sit unnoticed: a rule with no test in a given +/// language is not "known good", it is "never looked at". +use crate::common::*; +use anyhow::Result; + +/// A bare number set is spoken as a full Polish phrase. +#[test] +fn simple_number_sets() -> Result<()> { + let expr = r#" + ℂ, + ℕ, + ℚ, + ℝ, + ℤ + "#; + test("pl", "SimpleSpeak", expr, "liczby zespolone przecinek, liczby naturalne przecinek, liczby wymierne przecinek, liczby rzeczywiste, przecinek, liczby całkowite")?; + Ok(()) +} + +/// With a dimension the letter itself is spoken, as in every language except +/// Hungarian: "R 3", not "liczby rzeczywiste 3". +#[test] +fn dimension_number_sets() -> Result<()> { + let expr = r#" + ℝ3, + ℂ2, + ℕ4 + "#; + test("pl", "SimpleSpeak", expr, "R 3 przecinek, C 2 przecinek, N 4")?; + Ok(()) +} + +/// A non-numeric exponent is not a dimension, so the full phrase comes back. +/// This is the negative control for the rule above: it fires on `count(*)=2` +/// with a digit, and must not swallow every superscript. +#[test] +fn number_set_with_variable_exponent() -> Result<()> { + let expr = r#" + ℤn + "#; + test("pl", "SimpleSpeak", expr, "liczby całkowite do potęgi n")?; + Ok(()) +} + +/// Positive and negative variants (ℝ⁺, ℤ⁻) are a separate rule again. +#[test] +fn pos_neg_number_sets() -> Result<()> { + let expr = r#" + ℝ+, + ℤ- + "#; + test("pl", "SimpleSpeak", expr, "plus liczby rzeczywiste, przecinek; minus liczby całkowite")?; + Ok(()) +} + +/// ClearSpeak must agree with SimpleSpeak here - the rules live in +/// SharedRules, so a divergence would mean one of them stopped matching. +#[test] +fn number_sets_clearspeak_matches() -> Result<()> { + let expr = r#" + ℝ, + ℝ3 + "#; + test("pl", "ClearSpeak", expr, "liczby rzeczywiste, przecinek, R 3")?; + Ok(()) +} diff --git a/tests/Languages/pl/unit_plurals.rs b/tests/Languages/pl/unit_plurals.rs new file mode 100644 index 000000000..a3a6ec5b8 --- /dev/null +++ b/tests/Languages/pl/unit_plurals.rs @@ -0,0 +1,86 @@ +/// Polish unit plurals: the noun form follows the numeral, not just "is it one". +/// 1 metr | 2, 3, 4 metry (nominative plural) | 5+ metrów (genitive plural) +/// The teens are the exception that breaks the naive rule: 12, 13, 14 metrów. +use crate::common::*; +use anyhow::Result; + +/// The three numeral classes on a single unit, including the teens exception. +#[test] +fn unit_plural_numeral_classes() -> Result<()> { + let expr = r#" + 1m,2m, + 3m,4m, + 5m,11m, + 12m,13m, + 14m,22m, + 101m,102m, + 112m + "#; + test("pl", "SimpleSpeak", expr, "1 metr, przecinek; 2 metry, przecinek; 3 metry, przecinek; 4 metry, przecinek; 5 metrów, przecinek; 11 metrów, przecinek; 12 metrów, przecinek; 13 metrów, przecinek; 14 metrów, przecinek; 22 metry, przecinek; 101 metrów, przecinek; 102 metry, przecinek; 112 metrów")?; + Ok(()) +} + +/// SI base units: previously all of these took the English "s". +#[test] +fn unit_plural_si_base() -> Result<()> { + let expr = r#" + 2g,5g, + 2s,5s, + 2A,5A, + 2K,5K, + 2mol,5mol, + 2cd,5cd + "#; + test("pl", "SimpleSpeak", expr, "2 gramy, przecinek; 5 gramów, przecinek; 2 sekundy, przecinek; 5 sekund, przecinek; 2 ampery, przecinek; 5 amperów, przecinek; 2 kelwiny, przecinek; 5 kelwinów, przecinek; 2 mole, przecinek; 5 moli, przecinek; 2 kandele, przecinek; 5 kandeli")?; + Ok(()) +} + +/// With an SI prefix the unit goes through a different branch of the rule. +#[test] +fn unit_plural_with_prefix() -> Result<()> { + let expr = r#" + 2km,5km, + 2mg,5mg, + 2daA,5daA + "#; + test("pl", "SimpleSpeak", expr, "2 kilo-metry, przecinek; 5 kilo-metrów, przecinek; 2 mili-gramy, przecinek; 5 mili-gramów, przecinek; 2 deka-ampery, przecinek; 5 deka-amperów")?; + Ok(()) +} + +/// Irregular nouns: the plural cannot be derived from the ending. +#[test] +fn unit_plural_irregular() -> Result<()> { + let expr = r#" + 2d,5d, + 2w,5w, + 2yr,5yr, + 2ft,5ft + "#; + test("pl", "SimpleSpeak", expr, "2 dni, przecinek, 5 dni, przecinek; 2 tygodnie, przecinek; 5 tygodni, przecinek; 2 lata, przecinek, 5 lat, przecinek; 2 stopy, przecinek; 5 stóp")?; + Ok(()) +} + +/// Units whose base form was itself wrong (plural used as singular). +#[test] +fn unit_singular_forms_fixed() -> Result<()> { + let expr = r#" + 1Hz,2Hz, + 5Hz, + 1hp,2hp, + 1℧ + "#; + test("pl", "SimpleSpeak", expr, "1 herc, przecinek; 2 herce, przecinek; 5 herców, przecinek; 1 koń mechaniczny, przecinek; 2 konie mechaniczne, przecinek, 1 mho")?; + Ok(()) +} + +/// Multi-word units inflect the head noun and keep the dependent word's case. +#[test] +fn unit_plural_multiword() -> Result<()> { + let expr = r#" + 2ly,5ly, + 2au,5au, + 2arcmin,5arcmin + "#; + test("pl", "SimpleSpeak", expr, "2 lata świetlne, przecinek; 5 lat świetlnych, przecinek; 2 jednostki astronomiczne, przecinek; 5 jednostek astronomicznych; przecinek; 2 minuty łuku, przecinek; 5 minut łuku")?; + Ok(()) +} diff --git a/tests/Languages/pl/units.rs b/tests/Languages/pl/units.rs index 1676cf8ae..591e2eaff 100644 --- a/tests/Languages/pl/units.rs +++ b/tests/Languages/pl/units.rs @@ -40,7 +40,7 @@ fn prefix_sweep() -> Result<()> { qg "#; test("pl", "SimpleSpeak", expr, - "kwetta-grams, przecinek; ronna-grams, przecinek; jotta-grams, przecinek; zetta-grams, przecinek; eksa-grams, przecinek; peta-grams, przecinek; tera-grams, przecinek; giga-grams, przecinek; mega-grams, przecinek; kilo-grams, przecinek; hekto-grams, przecinek; deka-grams, przecinek; decy-grams, przecinek; centy-grams, przecinek; mili-grams, przecinek; mikro-grams, przecinek; nano-grams, przecinek; piko-grams, przecinek; femto-grams, przecinek; atto-grams, przecinek; zepto-grams, przecinek; jokto-grams, przecinek; ronto-grams, przecinek; kwekto-grams")?; + "kwetta-gramów, przecinek; ronna-gramów, przecinek; jotta-gramów, przecinek; zetta-gramów, przecinek; eksa-gramów, przecinek; peta-gramów, przecinek; tera-gramów, przecinek; giga-gramów, przecinek; mega-gramów, przecinek; kilo-gramów, przecinek; hekto-gramów, przecinek; deka-gramów, przecinek; decy-gramów, przecinek; centy-gramów, przecinek; mili-gramów, przecinek; mikro-gramów, przecinek; nano-gramów, przecinek; piko-gramów, przecinek; femto-gramów, przecinek; atto-gramów, przecinek; zepto-gramów, przecinek; jokto-gramów, przecinek; ronto-gramów, przecinek; kwekto-gramów")?; return Ok(()); } @@ -61,7 +61,7 @@ fn si_base() -> Result<()> { 1sec,2sec "#; test("pl", "SimpleSpeak", expr, - "1 amper, przecinek; 2 ampers, przecinek; 1 kandela, przecinek; 2 kandelas, przecinek; 1 kelwin, przecinek; 2 kelwins, przecinek; 1 kelwin, przecinek; 2 kelwins, przecinek; 1 gram, przecinek; 2 grams, przecinek; 1 metr, przecinek; 2 metrs, przecinek, 1 mol, przecinek; 2 mols, przecinek; 1 sekunda, przecinek; 2 sekundas, przecinek; 1 sekunda, przecinek; 2 sekundas, przecinek; 1 sekunda, przecinek; 2 sekundas, przecinek; 1 sekunda, przecinek; 2 sekundas")?; + "1 amper, przecinek; 2 ampery, przecinek; 1 kandela, przecinek; 2 kandele, przecinek; 1 kelwin, przecinek; 2 kelwiny, przecinek; 1 kelwin, przecinek; 2 kelwiny, przecinek; 1 gram, przecinek; 2 gramy, przecinek; 1 metr, przecinek; 2 metry, przecinek, 1 mol, przecinek; 2 mole, przecinek; 1 sekunda, przecinek; 2 sekundy, przecinek; 1 sekunda, przecinek; 2 sekundy, przecinek; 1 sekunda, przecinek; 2 sekundy, przecinek; 1 sekunda, przecinek; 2 sekundy")?; return Ok(()); } @@ -80,7 +80,7 @@ fn si_base_with_prefixes() -> Result<()> { 1nsec,2psec "#; test("pl", "SimpleSpeak", expr, - "1 kwetta-amper, przecinek; 2 ronna-ampers, przecinek; 1 jotta-kandela, przecinek; 2 zetta-kandelas; przecinek; 1 eksa-kelwin, przecinek; 2 peta-kelwins, przecinek; 1 tera-kelwin, przecinek; 2 giga-kelwins, przecinek; 1 mega-gram, przecinek; 2 kilo-grams, przecinek; 1 hekto-metr, przecinek; 2 deka-metrs, przecinek; 1 decy-mol, przecinek; 2 centy-mols, przecinek; 1 mili-sekunda, przecinek; 2 mikro-sekundas; przecinek; 1 nano-sekunda, przecinek; 2 piko-sekundas")?; + "1 kwetta-amper, przecinek; 2 ronna-ampery, przecinek; 1 jotta-kandela, przecinek; 2 zetta-kandele, przecinek; 1 eksa-kelwin, przecinek; 2 peta-kelwiny, przecinek; 1 tera-kelwin, przecinek; 2 giga-kelwiny, przecinek; 1 mega-gram, przecinek; 2 kilo-gramy, przecinek; 1 hekto-metr, przecinek; 2 deka-metry, przecinek; 1 decy-mol, przecinek; 2 centy-mole, przecinek; 1 mili-sekunda, przecinek; 2 mikro-sekundy, przecinek; 1 nano-sekunda, przecinek; 2 piko-sekundy")?; return Ok(()); } @@ -103,7 +103,7 @@ fn si_derived_1() -> Result<()> { 1lx,2lx "#; test("pl", "SimpleSpeak", expr, - "1 bekerel, przecinek; 2 bekerels, przecinek; 1 kulomb, przecinek; 2 kulombs, przecinek; 1 stopień Celsjusza, przecinek; 2 stopnie Celsjusza, przecinek; 1 stopień Celsjusza, przecinek; 2 stopnie Celsjusza, przecinek; 1 farad, przecinek; 2 farads, przecinek; 1 grej, przecinek; 2 grejs, przecinek; 1 henr, przecinek; 2 henry, przecinek; 1 herce, przecinek; 2 herces, przecinek; 1 dżul, przecinek; 2 dżuls, przecinek; 1 katal, przecinek; 2 katals, przecinek; 1 lumen, przecinek; 2 lumens, przecinek; 1 luks, przecinek; 2 luksy")?; + "1 bekerel, przecinek; 2 bekerele, przecinek; 1 kulomb, przecinek; 2 kulomby, przecinek; 1 stopień Celsjusza, przecinek; 2 stopnie Celsjusza, przecinek; 1 stopień Celsjusza, przecinek; 2 stopnie Celsjusza, przecinek; 1 farad, przecinek; 2 farady, przecinek; 1 grej, przecinek; 2 greje, przecinek; 1 henr, przecinek; 2 henry, przecinek; 1 herc, przecinek; 2 herce, przecinek; 1 dżul, przecinek; 2 dżule, przecinek; 1 katal, przecinek; 2 katale, przecinek; 1 lumen, przecinek; 2 lumeny, przecinek; 1 luks, przecinek; 2 luksy")?; return Ok(()); } @@ -125,7 +125,7 @@ fn si_derived_1_with_prefixes() -> Result<()> { 1p℃,2n℃ "#; test("pl", "SimpleSpeak", expr, - "1 kwetta-bekerel, przecinek; 2 ronna-bekerels; przecinek; 1 jotta-kulomb, przecinek; 2 zetta-kulombs; przecinek; 1 eksa-farad, przecinek; 2 peta-farads, przecinek; 1 tera-grej, przecinek; 2 giga-grejs, przecinek; 1 mega-henr, przecinek; 2 kilo-henry, przecinek; 1 deka-herce, przecinek; 2 decy-herces, przecinek; 1 centy-dżul, przecinek; 2 mili-dżuls, przecinek; 1 mikro-katal, przecinek; 2 nano-katals, przecinek; 1 piko-lumen, przecinek; 2 femto-lumens, przecinek; 1 atto-luks, przecinek; 2 zepto-luksy, przecinek; 1 mili-stopień Celsjusza; przecinek; 2 mikro-stopnie Celsjusza; przecinek; 1 piko-stopień Celsjusza; przecinek; 2 nano-stopnie Celsjusza")?; + "1 kwetta-bekerel, przecinek; 2 ronna-bekerele, przecinek; 1 jotta-kulomb, przecinek; 2 zetta-kulomby, przecinek; 1 eksa-farad, przecinek; 2 peta-farady, przecinek; 1 tera-grej, przecinek; 2 giga-greje, przecinek; 1 mega-henr, przecinek; 2 kilo-henry, przecinek; 1 deka-herc, przecinek; 2 decy-herce, przecinek; 1 centy-dżul, przecinek; 2 mili-dżule, przecinek; 1 mikro-katal, przecinek; 2 nano-katale, przecinek; 1 piko-lumen, przecinek; 2 femto-lumeny, przecinek; 1 atto-luks, przecinek; 2 zepto-luksy, przecinek; 1 mili-stopień Celsjusza; przecinek; 2 mikro-stopnie Celsjusza; przecinek; 1 piko-stopień Celsjusza; przecinek; 2 nano-stopnie Celsjusza")?; return Ok(()); } @@ -145,7 +145,7 @@ fn si_derived_2() -> Result<()> { 1Wb,2Wb "#; test("pl", "SimpleSpeak", expr, - "1 niuton, przecinek; 2 niutons, przecinek, 1 om przecinek; 2 oms, przecinek, 1 om przecinek; 2 oms, przecinek; 1 paskal, przecinek; 2 paskals, przecinek; 1 siemens, przecinek; 2 siemens, przecinek; 1 siwert, przecinek; 2 siwerts, przecinek; 1 tesla, przecinek; 2 teslas, przecinek; 1 wolt, przecinek; 2 wolty, przecinek, 1 wat, przecinek; 2 waty, przecinek; 1 weber, przecinek; 2 webers")?; + "1 niuton, przecinek; 2 niutony, przecinek, 1 om przecinek, 2 omy, przecinek, 1 om przecinek, 2 omy, przecinek; 1 paskal, przecinek; 2 paskale, przecinek; 1 siemens, przecinek; 2 siemensy, przecinek; 1 siwert, przecinek; 2 siwerty, przecinek; 1 tesla, przecinek; 2 tesle, przecinek; 1 wolt, przecinek; 2 wolty, przecinek, 1 wat, przecinek; 2 waty, przecinek; 1 weber, przecinek; 2 webery")?; return Ok(()); } @@ -165,7 +165,7 @@ fn si_derived_2_with_prefixes() -> Result<()> { 1PWb,2EWb "#; test("pl", "SimpleSpeak", expr, - "1 kwekto-niuton, przecinek; 2 ronto-niutons; przecinek; 1 jokto-om, przecinek; 2 zepto-oms, przecinek; 1 atto-om, przecinek; 2 femto-oms, przecinek; 1 piko-paskal, przecinek; 2 nano-paskals, przecinek; 1 mikro-siemens, przecinek; 2 mili-siemens, przecinek; 1 centy-siwert, przecinek; 2 decy-siwerts, przecinek; 1 deka-tesla, przecinek; 2 hekto-teslas, przecinek; 1 kilo-wolt, przecinek; 2 mega-wolty, przecinek; 1 giga-wat, przecinek; 2 tera-waty, przecinek; 1 peta-weber, przecinek; 2 eksa-webers")?; + "1 kwekto-niuton, przecinek; 2 ronto-niutony, przecinek; 1 jokto-om, przecinek; 2 zepto-omy, przecinek; 1 atto-om, przecinek; 2 femto-omy, przecinek; 1 piko-paskal, przecinek; 2 nano-paskale, przecinek; 1 mikro-siemens, przecinek; 2 mili-siemensy, przecinek; 1 centy-siwert, przecinek; 2 decy-siwerty, przecinek; 1 deka-tesla, przecinek; 2 hekto-tesle, przecinek; 1 kilo-wolt, przecinek; 2 mega-wolty, przecinek; 1 giga-wat, przecinek; 2 tera-waty, przecinek; 1 peta-weber, przecinek; 2 eksa-webery")?; return Ok(()); } @@ -191,7 +191,7 @@ fn si_accepted() -> Result<()> { 1Bd,2Bd "#; test("pl", "SimpleSpeak", expr, - "1 litr, przecinek; 2 litrs, przecinek; 1 litr, przecinek; 2 litrs, przecinek; 1 litr, przecinek; 2 litrs, przecinek; 1 tona, przecinek; 2 tonas, przecinek; 1 dalton, przecinek; 2 daltons, przecinek; 1 neper, przecinek; 2 nepers, przecinek; 1 jednostka masy atomowej, przecinek; 2 jednostka masy atomowejs; przecinek; 1 elektronowolt, przecinek; 2 elektronowolts, przecinek; 1 radian, przecinek; 2 radians, przecinek; 1 steradian, przecinek; 2 steradians, przecinek, 1 rok, przecinek; 2 roks, przecinek; 1 sekunda łuku, przecinek; 2 sekunda łukus, przecinek, 1 bit, przecinek; 2 bits, przecinek; 1 bajt, przecinek; 2 bajts, przecinek, 1 bod, przecinek; 2 bods")?; + "1 litr, przecinek; 2 litry, przecinek; 1 litr, przecinek; 2 litry, przecinek; 1 litr, przecinek; 2 litry, przecinek; 1 tona, przecinek; 2 tony, przecinek; 1 dalton, przecinek; 2 daltony, przecinek; 1 neper, przecinek; 2 nepery, przecinek; 1 jednostka masy atomowej, przecinek; 2 jednostki masy atomowej, przecinek; 1 elektronowolt, przecinek; 2 elektronowolty, przecinek; 1 radian, przecinek; 2 radiany, przecinek; 1 steradian, przecinek; 2 steradiany, przecinek, 1 rok, przecinek; 2 lata, przecinek; 1 sekunda łuku, przecinek; 2 sekundy łuku, przecinek, 1 bit, przecinek; 2 bity, przecinek; 1 bajt, przecinek; 2 bajty, przecinek, 1 bod, przecinek; 2 body")?; return Ok(()); } @@ -216,7 +216,7 @@ fn si_accepted_with_prefixes() -> Result<()> { 1TBd,2EBd "#; test("pl", "SimpleSpeak", expr, - "1 kwetta-litr, przecinek; 2 ronna-litrs, przecinek; 1 jotta-litr, przecinek; 2 zetta-litrs, przecinek; 1 eksa-litr, przecinek; 2 peta-litrs, przecinek; 1 tera-tona, przecinek; 2 giga-tonas, przecinek; 1 mega-dalton, przecinek; 2 kilo-daltons, przecinek; 1 decy-neper, przecinek; 2 centy-nepers, przecinek; 1, hekto-jednostka masy atomowej; przecinek; 2, deka-jednostka masy atomowejs; przecinek; 1 mili-elektronowolt; przecinek; 2 mikro-elektronowolts; przecinek; 1 nano-radian, przecinek; 2 piko-radians, przecinek; 1 femto-steradian, przecinek; 2 atto-steradians; przecinek; 1 giga-rok, przecinek; 2 mega-roks, przecinek; 1 zepto-sekunda łuku; przecinek; 2 jokto-sekunda łukus; przecinek; 1 kilo-bit, przecinek; 2 mega-bits, przecinek; 1 giga-bajt, przecinek; 2 tera-bajts, przecinek; 1 tera-bod, przecinek; 2 eksa-bods")?; + "1 kwetta-litr, przecinek; 2 ronna-litry, przecinek; 1 jotta-litr, przecinek; 2 zetta-litry, przecinek; 1 eksa-litr, przecinek; 2 peta-litry, przecinek; 1 tera-tona, przecinek; 2 giga-tony, przecinek; 1 mega-dalton, przecinek; 2 kilo-daltony, przecinek; 1 decy-neper, przecinek; 2 centy-nepery, przecinek; 1, hekto-jednostka masy atomowej; przecinek; 2, deka-jednostki masy atomowej; przecinek; 1 mili-elektronowolt; przecinek; 2 mikro-elektronowolty; przecinek; 1 nano-radian, przecinek; 2 piko-radiany, przecinek; 1 femto-steradian, przecinek; 2 atto-steradiany, przecinek; 1 giga-rok, przecinek; 2 mega-lata, przecinek; 1 zepto-sekunda łuku; przecinek; 2 jokto-sekundy łuku; przecinek; 1 kilo-bit, przecinek; 2 mega-bity, przecinek; 1 giga-bajt, przecinek; 2 tera-bajty, przecinek; 1 tera-bod, przecinek; 2 eksa-body")?; return Ok(()); } @@ -240,7 +240,7 @@ fn without_prefix_time() -> Result<()> { 1yr,2yr "#; test("pl", "SimpleSpeak", expr, - "1 sekunda, przecinek; 2 sekundas, przecinek; 1 sekunda, przecinek; 2 sekundas, przecinek; 1 minuta, przecinek; 2 minutas, przecinek; 1 minuta, przecinek; 2 minutas, przecinek; 1 minuta, przecinek; 2 minutas, przecinek; 1 godzina, przecinek; 2 godzinas, przecinek; 1 godzina, przecinek; 2 godzinas, przecinek; 1 godzina, przecinek; 2 godzinas, przecinek; 1 dzień, przecinek; 2 dzieńs, przecinek; 1 dzień, przecinek; 2 dzieńs, przecinek; 1 tydzień, przecinek; 2 tydzieńs, przecinek; 1 tydzień, przecinek; 2 tydzieńs, przecinek, 1 rok, przecinek; 2 roks, przecinek, 1 rok, przecinek; 2 roks")?; + "1 sekunda, przecinek; 2 sekundy, przecinek; 1 sekunda, przecinek; 2 sekundy, przecinek; 1 minuta, przecinek; 2 minuty, przecinek; 1 minuta, przecinek; 2 minuty, przecinek; 1 minuta, przecinek; 2 minuty, przecinek; 1 godzina, przecinek; 2 godziny, przecinek; 1 godzina, przecinek; 2 godziny, przecinek; 1 godzina, przecinek; 2 godziny, przecinek; 1 dzień, przecinek, 2 dni, przecinek; 1 dzień, przecinek, 2 dni, przecinek; 1 tydzień, przecinek; 2 tygodnie, przecinek; 1 tydzień, przecinek; 2 tygodnie, przecinek, 1 rok, przecinek; 2 lata, przecinek, 1 rok, przecinek; 2 lata")?; return Ok(()); } @@ -258,7 +258,7 @@ fn without_prefix_angles() -> Result<()> { 1asec,2asec "#; test("pl", "SimpleSpeak", expr, - "1 stopień, przecinek; 2 stopnie, przecinek; 1 stopień, przecinek; 2 stopnie, przecinek; 1 minuta łuku, przecinek; 2 minuta łukus, przecinek; 1 minuta łuku, przecinek; 2 minuta łukus, przecinek; 1 minuta łuku, przecinek; 2 minuta łukus, przecinek; 1 minuta łuku, przecinek; 2 minuta łukus, przecinek; 1 sekunda łuku, przecinek; 2 sekunda łukus, przecinek; 1 sekunda łuku, przecinek; 2 sekunda łukus")?; + "1 stopień, przecinek; 2 stopnie, przecinek; 1 stopień, przecinek; 2 stopnie, przecinek; 1 minuta łuku, przecinek; 2 minuty łuku, przecinek; 1 minuta łuku, przecinek; 2 minuty łuku, przecinek; 1 minuta łuku, przecinek; 2 minuty łuku, przecinek; 1 minuta łuku, przecinek; 2 minuty łuku, przecinek; 1 sekunda łuku, przecinek; 2 sekundy łuku, przecinek; 1 sekunda łuku, przecinek; 2 sekundy łuku")?; return Ok(()); } @@ -274,7 +274,7 @@ fn without_prefix_distance() -> Result<()> { 1fm,2fm "#; test("pl", "SimpleSpeak", expr, - "1 jednostka astronomiczna, przecinek; 2 jednostka astronomicznas; przecinek; 1 rok świetlny, przecinek; 2 rok świetlnys, przecinek; 1 parsek, przecinek; 2 parseks, przecinek; 1 angstrem, przecinek; 2 angstrems, przecinek; 1 angstrem, przecinek; 2 angstrems, przecinek; 1 fermi, przecinek; 2 fermis")?; + "1 jednostka astronomiczna, przecinek; 2 jednostki astronomiczne, przecinek; 1 rok świetlny, przecinek; 2 lata świetlne, przecinek; 1 parsek, przecinek; 2 parseki, przecinek; 1 angstrem, przecinek; 2 angstremy, przecinek; 1 angstrem, przecinek; 2 angstremy, przecinek; 1 fermi, przecinek; 2 fermi")?; return Ok(()); } @@ -299,7 +299,7 @@ fn without_prefix_other() -> Result<()> { 1erg,2erg "#; test("pl", "SimpleSpeak", expr, - "1 hektar, przecinek; 2 hektars, przecinek; 1 decybel, przecinek; 2 decybels, przecinek; 1 atmosfera, przecinek; 2 atmosferas, przecinek; 1 jednostka masy atomowej, przecinek; 2 jednostka masy atomowejs; przecinek, 1 bar, przecinek; 2 bars, przecinek; 1 kaloria, przecinek; 2 kalorias, przecinek; 1 kiur, przecinek; 2 kiurs, przecinek; 1 grad, przecinek; 2 grads, przecinek; 1 molowy, przecinek; 2 molowys, przecinek; 1 rentgen, przecinek; 2 rentgens, przecinek; 1 obrót na minutę, przecinek; 2 obroty na minutę, przecinek; 1 drachma płynu, przecinek; 2 drachma płynus, przecinek; 1 M h o, przecinek; 2 M h os, przecinek; 1 dyna, przecinek; 2 dynas, przecinek, 1 erg, przecinek; 2 ergs")?; + "1 hektar, przecinek; 2 hektary, przecinek; 1 decybel, przecinek; 2 decybele, przecinek; 1 atmosfera, przecinek; 2 atmosfery, przecinek; 1 jednostka masy atomowej, przecinek; 2 jednostki masy atomowej, przecinek, 1 bar, przecinek; 2 bary, przecinek; 1 kaloria, przecinek; 2 kalorie, przecinek; 1 kiur, przecinek; 2 kiury, przecinek; 1 grad, przecinek; 2 grady, przecinek; 1 molowy, przecinek; 2 molowe, przecinek; 1 rentgen, przecinek; 2 rentgeny, przecinek; 1 obrót na minutę, przecinek; 2 obroty na minutę, przecinek; 1 drachma płynu, przecinek; 2 drachmy płynu, przecinek, 1 mho, przecinek, 2 mho, przecinek; 1 dyna, przecinek; 2 dyny, przecinek, 1 erg, przecinek; 2 ergi")?; return Ok(()); } @@ -325,7 +325,7 @@ fn without_prefix_powers_of_2() -> Result<()> { 1YiB,2YiB "#; test("pl", "SimpleSpeak", expr, - "1 kibibit, przecinek; 2 kibibits, przecinek; 1 mebibit, przecinek; 2 mebibits, przecinek; 1 gibibit, przecinek; 2 gibibits, przecinek; 1 tebibit, przecinek; 2 tebibits, przecinek; 1 pebibit, przecinek; 2 pebibits, przecinek; 1 eksbibit, przecinek; 2 eksbibits, przecinek; 1 zebibit, przecinek; 2 zebibits, przecinek; 1 jobibit, przecinek; 2 jobibits, przecinek; 1 kibibajt, przecinek; 2 kibibajts, przecinek; 1 mebibajt, przecinek; 2 mebibajts, przecinek; 1 gibibajt, przecinek; 2 gibibajts, przecinek; 1 tebibajt, przecinek; 2 tebibajts, przecinek; 1 pebibajt, przecinek; 2 pebibajts, przecinek; 1 eksbibajt, przecinek; 2 eksbibajts, przecinek; 1 zebibajt, przecinek; 2 zebibajts, przecinek; 1 jobibajt, przecinek; 2 jobibajts")?; + "1 kibibit, przecinek; 2 kibibity, przecinek; 1 mebibit, przecinek; 2 mebibity, przecinek; 1 gibibit, przecinek; 2 gibibity, przecinek; 1 tebibit, przecinek; 2 tebibity, przecinek; 1 pebibit, przecinek; 2 pebibity, przecinek; 1 eksbibit, przecinek; 2 eksbibity, przecinek; 1 zebibit, przecinek; 2 zebibity, przecinek; 1 jobibit, przecinek; 2 jobibity, przecinek; 1 kibibajt, przecinek; 2 kibibajty, przecinek; 1 mebibajt, przecinek; 2 mebibajty, przecinek; 1 gibibajt, przecinek; 2 gibibajty, przecinek; 1 tebibajt, przecinek; 2 tebibajty, przecinek; 1 pebibajt, przecinek; 2 pebibajty, przecinek; 1 eksbibajt, przecinek; 2 eksbibajty, przecinek; 1 zebibajt, przecinek; 2 zebibajty, przecinek; 1 jobibajt, przecinek; 2 jobibajty")?; return Ok(()); } @@ -342,11 +342,11 @@ fn si_other_numbers() -> Result<()> { 2.5µsec, 32.34mol"#; test_prefs("pl", "SimpleSpeak", vec![("Verbosity", "Terse")], expr, - "10 l przecinek, 20 m przecinek; x mili-sekundas; przecinek; y mikro-sekundas; przecinek; deka-grams, przecinek; 1235 deka-niutons; przecinek; 25 mikro-sekundas; przecinek; 3234 mols")?; + "10 l przecinek, 20 m przecinek; x mili-sekund, przecinek; y mikro-sekund, przecinek; deka-gramów, przecinek; 1235 deka-niutonów; przecinek; 25 mikro-sekundy, przecinek; 3234 mole")?; test_prefs("pl", "ClearSpeak", vec![("Verbosity", "Medium")], expr, - "10 litr, przecinek; 20 metrs, przecinek; x mili-sekundas; przecinek; y mikro-sekundas; przecinek; deka-grams, przecinek; 1235 deka-niutons; przecinek; 25 mikro-sekundas; przecinek; 3234 mols")?; + "10 litr, przecinek; 20 metry, przecinek; x mili-sekund, przecinek; y mikro-sekund, przecinek; deka-gramów, przecinek; 1235 deka-niutonów; przecinek; 25 mikro-sekundy, przecinek; 3234 mole")?; test_prefs("pl", "SimpleSpeak", vec![("Verbosity", "Verbose")], expr, - "10 litr, przecinek; 20 metrs, przecinek; x mili-sekundas; przecinek; y mikro-sekundas; przecinek; deka-grams, przecinek; 1235 deka-niutons; przecinek; 25 mikro-sekundas; przecinek; 3234 mols")?; + "10 litr, przecinek; 20 metry, przecinek; x mili-sekund, przecinek; y mikro-sekund, przecinek; deka-gramów, przecinek; 1235 deka-niutonów; przecinek; 25 mikro-sekundy, przecinek; 3234 mole")?; return Ok(()); } @@ -361,7 +361,7 @@ fn test_mtext_inference() -> Result<()> { 4.5mT ]"#; test("pl", "SimpleSpeak", expr, - "nawias kwadratowy otwierający; 1 tona, przecinek; 2 peta-ampers, przecinek; 3 paskals, przecinek; 45 mili-teslas; nawias kwadratowy zamykający")?; + "nawias kwadratowy otwierający; 1 tona, przecinek; 2 peta-ampery, przecinek; 3 paskale, przecinek; 45 mili-tesli; nawias kwadratowy zamykający")?; return Ok(()); } @@ -376,7 +376,7 @@ fn test_mtext_inference() -> Result<()> { mmin "#; test("pl", "SimpleSpeak", expr, - "3 metrs, przecinek; 1 kilo-metr, przecinek; 3 metrs, przecinek; 3 dziesiąte farads, przecinek; m indeks dolny minimum koniec indeksu dolnego")?; + "3 metry, przecinek; 1 kilo-metr, przecinek; 3 metry, przecinek; 3 dziesiąte faradów, przecinek; m indeks dolny minimum koniec indeksu dolnego")?; return Ok(()); } From 9f4721a517eb986c318f1f113531d9cc648e0882 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dziwisz?= Date: Thu, 3 Sep 2026 13:15:05 +0200 Subject: [PATCH 087/120] pl: speak the unit after "na" in the accusative, not the nominative (#745) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * pl: mark the unicode translations as verified (t -> T) Mechanical change: 2435 text keys raised from t/ot/ct to T/OT/CT in the two unicode files. No text is touched. The Polish rules were written before the lowercase/uppercase convention was in use, so entries that have been translated all along still carried the "needs review" key. The audit tool therefore reported them as untranslated, which made the real gaps impossible to see: audit-translations pl, untranslated text: 3162 -> 727 The remaining 727 are in the rule files, where the entries need reading rather than a mechanical pass, so they are deliberately left alone. Every raised entry was checked to differ from the English source, i.e. it really is translated. Entries whose text legitimately equals English (Roman numerals, ligatures, proper names, whitespace) were handled separately in the previous PR; entries still holding English text are NOT raised, since that would assert a translation that does not exist. Verification: cargo test --test languages Languages::pl 609 passed, 0 failed diff: 2436 insertions, 2436 deletions, zero changes outside key case The last point is checked by normalising the key case in the diff and confirming no unpaired lines remain (without normalising, 4710 remain, so the check does discriminate). * pl: also mark the multi-line unicode entries as verified The previous commit raised the key on entries written on a single line (`- "±": [t: "..."]`), but not on entries whose body is a block: - "°": - test: if: "parent::*[name(.)='msup' ...]" then: [t: "stopni"] else: [t: "stopień"] Those are exactly the entries carrying inflection or a TTS condition, so 312 of the most deliberate translations in the file were left looking unreviewed. This raises them: 64 in unicode.yaml, 247 in unicode-full.yaml and one single-line entry (U+23E6) missed earlier. One entry is deliberately left with a lower-case key: `t: ""` for U+00A0, the else-branch of the `@data-empty-in-2D` test. That is silence rather than text, and an upper-case key means "a translator checked this wording" - there is no wording to check. English keeps a lower-case key there too. No content changes; only key case. `cargo test --test languages Languages::pl` gives 609 passed, 0 failed, which is the same result as before this commit, as expected for a change the engine treats as equivalent (src/speech.rs matches "t"|"T" in one match arm). * pl: raise the remaining rule-file keys and test the number-set rules Two loose ends in the Polish localization. **Rule-file keys.** 248 entries in the rule files still used a lower-case key while carrying translated Polish text. Content is untouched - every one of the 248 changed lines differs from its predecessor only in the case of the key name, verified line by line after normalizing the key. 17 entries are deliberately left lower-case: five are the letters C, N, Q, R, Z in `dimension-number-sets` (the letter itself is the wording, as in English); `ct: "s"`/`ct: "-"` in the unit rule are the plural suffix and prefix separator; `navigate.yaml:176` is a Polish euphony variant ("ze" before "st-"); the remaining seven are XPath expressions rather than speech. **Tests for the number-set rules.** These rules had no Polish test at all, in a file where two silent defects were already found. New `tests/Languages/pl/number_sets.rs` covers the bare symbols, the dimension form, both sign variants, and ClearSpeak agreeing with SimpleSpeak. The `number_set_with_variable_exponent` case is the negative control: `dimension-number-sets` fires on `count(*)=2`, so a non-numeric exponent must fall through to the full phrase ("liczby całkowite do potęgi n") rather than being read as a dimension. Without it, a rule that swallowed every superscript would still pass. Polish keeps the letter with a dimension ("R 3"), matching en, de, fr, nb, sv, fi and es; Hungarian is the only language that translates in that position too. Measured with the SAPI voice NVDA actually uses: "R 3" is byte-identical to a phonetic "er 3" and "N 4" to "en 4", so the letters are pronounced as their Polish names. cargo test --test languages Languages::pl 614 passed, 0 failed * pl: inflect unit names according to the numeral, not with an English "s" The `unit` rule appended `ct: "s"` whenever a unit had no entry in `PluralForms`, and the Polish `PluralForms` contained no SI base unit at all. Measured output of the engine before this change: 1 metr, 2 metrs, 5 metrs, 21 metrs, 2 grams, 2 sekundas, 2 ampers, 2 mols, 2 kilo-metrs So a Polish NVDA user heard an English plural ending on essentially every quantity. Hungarian avoids this with `CT: ""` (it appends nothing); French and German legitimately use "s"; Russian, whose plurals work like Polish, does not rely on a suffix at all and fills in `PluralForms` instead. Polish was in the wrong group. Polish does not have one plural form. The noun follows the numeral: 1 metr | 2, 3, 4 metry (nominative plural) | 5 metrów (genitive plural) and the teens are the exception that breaks the naive rule: 12, 13, 14 metrów, likewise 112-114, while 22 and 102 take the nominative again. One extra plural column is therefore not enough, so this adds a second definition section, `PluralGenitive`, next to `PluralForms` (now the nominative plural). No Rust change is needed: `build_values` accepts any definition name as a hashmap. The `unit` rule (both the bare and the prefixed branch) and the `currency` rule now read the value of the numeral and pick the section accordingly. When the numeral cannot be determined - a variable coefficient, as in "x ms" - `$Coefficient` is 0 and the genitive is used, which is also what Polish uses for unknown quantities ("x mili-sekund"). Where no inflected form is known the singular is spoken rather than a guessed ending: a wrong ending is worse for a screen reader user than a missing one. Both sections cover all 131 unit and currency names that the Polish `SIUnits`, `UnitsWithoutPrefixes`, `EnglishUnits` and `CurrencySymbols` can produce. The forms are written out because Polish inflection cannot be derived from the ending: metr/metry but litr/litry, om/omy, dzień/dni, tydzień/tygodnie, rok/lata. Multi-word units inflect the head noun and keep the dependent word's case ("5 lat świetlnych", "5 minut łuku"). They were checked against hunspell pl_PL; the only words it does not know are the binary-prefix neologisms (kibibajt, gibibit, ...) whose singular forms already existed in the file. Three base forms were themselves wrong - plurals used as singulars, so the engine said "1 herce" and "1 konie mechaniczne": Hz herce -> herc hp konie mechaniczne -> koń mechaniczny ℧ M h o -> mho Every other language has a singular here (en hertz, ru герц, fi hertsi, es hercio; hp: ru "лошадиная сила", hu "lóerő", nb "hestekraft"; ℧: de/fr "mho"). Why this survived: the existing Polish expectations in tests/Languages/pl/units.rs recorded the broken output, so 609 tests passed while the speech was wrong ("2 dzieńs", "2 tydzieńs", "kwetta-grams"). Expectations harvested from real engine output are a good regression net but prove nothing about correctness unless they are read as a user would hear them. 21 test expectations are updated here, each difference reviewed word by word. Verification: cargo test --test languages Languages::pl 615 passed, 0 failed reverting general.yaml alone -> 6 of 6 new tests fail The new tests in tests/Languages/pl/unit_plurals.rs cover one class of phenomenon each: the three numeral classes including the teens exception, SI base units, the prefixed branch of the rule, irregular nouns, the corrected base forms, and multi-word units. * pl: speak the unit after "na" in the accusative, not the nominative The `per-fraction` rule speaks the denominator of a unit fraction after the preposition "na", which in Polish governs the accusative case. The unit was inserted in the nominative, so the engine said: 62 mile na godzina (should be "na godzinę") 5 metrów na sekunda (should be "na sekundę") 3 galony na mila (should be "na milę") 3 metry na sekunda do kwadratu Only feminine nouns change form here. For masculine inanimate and neuter names the accusative already equals the nominative, so "3 gramy na litr" and "1 dżul na mol" were correct and stay untouched. That narrows the work to 32 of the 131 unit and currency names, which is why `AccusativeCase` deliberately holds only those - a missing entry means "the form does not change". Why this lives in the `unit` rule rather than in `per-fraction`: Russian has the same problem and solves it inside `per-fraction` by testing the unit symbol and writing the inflected word inline (`милю`), which works because only one word is affected. Polish would need 32 such cases in the rule. Instead the `unit` rule asks whether it is being spoken as a fraction denominator, so the choice is made once, in one place, for every name. The denominator can also be raised to a power, as in m/s², and then the unit's parent is the `power` element rather than the fraction itself, so `IsFractionDenominator` tests both shapes. That second case was found only after the first fix: the existing expectation "3 metry na sekunda do kwadratu" was still passing green, because the list of failing tests shows only the cases whose output actually changed. Searching every Polish expectation for a nominative after "na" is what surfaced it. An adjective agrees with its noun ("na stopę kwadratową") while a dependent noun keeps its own case ("na minutę łuku", "na uncję płynu"), so the forms are written out rather than derived. Verification: cargo test --test languages Languages::pl 620 passed, 0 failed New tests in tests/Languages/pl/preposition_case.rs include two negative controls: masculine and neuter denominators must NOT change, and a unit standing on its own must stay in the nominative, so the accusative cannot leak outside the fraction denominator. --------- Co-authored-by: NSoiffer --- Rules/Languages/pl/SharedRules/general.yaml | 16 ++++- Rules/Languages/pl/definitions.yaml | 42 ++++++++++++ tests/Languages/pl.rs | 1 + tests/Languages/pl/ClearSpeak/mfrac.rs | 2 +- tests/Languages/pl/SimpleSpeak/mfrac.rs | 8 +-- tests/Languages/pl/preposition_case.rs | 71 +++++++++++++++++++++ 6 files changed, 133 insertions(+), 7 deletions(-) create mode 100644 tests/Languages/pl/preposition_case.rs diff --git a/Rules/Languages/pl/SharedRules/general.yaml b/Rules/Languages/pl/SharedRules/general.yaml index f3d026f2b..e6c94a36a 100644 --- a/Rules/Languages/pl/SharedRules/general.yaml +++ b/Rules/Languages/pl/SharedRules/general.yaml @@ -293,6 +293,14 @@ # cannot be determined, which falls through to the genitive - the form # Polish uses with unknown or large quantities. - Coefficient: "IfThenElse($MRowForCoefficient, number($MRowForCoefficient/*[1][self::m:mn]), 0)" + # The 'per-fraction' rule speaks the denominator after the preposition "na", + # which governs the accusative: "na godzinę", not "na godzina". Only feminine + # nouns change form, so AccusativeCase holds just those; a missing entry + # means the accusative equals the nominative (as for "na litr", "na mol"). + # The second test covers a denominator raised to a power, as in m/s^2, where + # the unit's parent is the 'power' element rather than the fraction itself. + - IsFractionDenominator: "(parent::m:fraction and preceding-sibling::*) or + (parent::m:power and parent::*[parent::m:fraction and preceding-sibling::*])" - IsNominativePlural: "$Coefficient mod 10 >= 2 and $Coefficient mod 10 <= 4 and not($Coefficient mod 100 >= 12 and $Coefficient mod 100 <= 14)" - Prefix: "''" @@ -339,7 +347,9 @@ if: "$Prefix = ''" then: - test: - - if: "$IsSingular" + - if: "$IsSingular and $IsFractionDenominator and DefinitionValue($Word, 'Speech', 'AccusativeCase') != ''" + then: [x: "DefinitionValue($Word, 'Speech', 'AccusativeCase')"] + - else_if: "$IsSingular" # HACK: '\uF8FE' is used internally for the concatenation char by 'ct' -- this gets the prefix concatenated to the base then: [x: "$Word"] - else_if: "$IsNominativePlural and DefinitionValue($Word, 'Speech', 'PluralForms') != ''" @@ -353,7 +363,9 @@ - x: "$Prefix" - ct: "-" - test: - - if: "$IsSingular" + - if: "$IsSingular and $IsFractionDenominator and DefinitionValue($Word, 'Speech', 'AccusativeCase') != ''" + then: [x: "concat('\uF8FE', DefinitionValue($Word, 'Speech', 'AccusativeCase'))"] + - else_if: "$IsSingular" # HACK: '\uF8FE' is used internally for the concatenation char by 'ct' -- this gets the prefix concatenated to the base then: [x: "concat('\uF8FE', $Word)"] - else_if: "$IsNominativePlural and DefinitionValue($Word, 'Speech', 'PluralForms') != ''" diff --git a/Rules/Languages/pl/definitions.yaml b/Rules/Languages/pl/definitions.yaml index 18785562b..0f212c4e4 100644 --- a/Rules/Languages/pl/definitions.yaml +++ b/Rules/Languages/pl/definitions.yaml @@ -646,6 +646,48 @@ "łyżka stołowa": "łyżki stołowe" } +- AccusativeCase: { + # Accusative singular, used after the preposition "na" in the + # 'per-fraction' rule: "62 mile na godzinę", not "na godzina". + # Only feminine nouns differ from the nominative, so masculine and + # neuter names are deliberately absent - a missing entry means + # "the form does not change" (na litr, na mol, na metr). + # An adjective agrees with its noun (milę kwadratową), while a + # dependent noun keeps its own case (uncję płynu, minutę łuku). + "atmosfera": "atmosferę", + "baryłka": "baryłkę", + "drachma": "drachmę", + "drachma płynu": "drachmę płynu", + "dyna": "dynę", + "godzina": "godzinę", + "jednostka astronomiczna": "jednostkę astronomiczną", + "jednostka masy atomowej": "jednostkę masy atomowej", + "kaloria": "kalorię", + "kandela": "kandelę", + "kwarta": "kwartę", + "mila": "milę", + "mila kwadratowa": "milę kwadratową", + "mila na galon": "milę na galon", + "mila na godzinę": "milę na godzinę", + "minuta": "minutę", + "minuta łuku": "minutę łuku", + "pinta": "pintę", + "rupia": "rupię", + "sekunda": "sekundę", + "sekunda łuku": "sekundę łuku", + "stopa": "stopę", + "stopa deskowa": "stopę deskową", + "stopa kwadratowa": "stopę kwadratową", + "stopa sześcienna": "stopę sześcienną", + "tesla": "teslę", + "tona": "tonę", + "uncja": "uncję", + "uncja płynu": "uncję płynu", + "uncja trojańska": "uncję trojańską", + "łyżeczka": "łyżeczkę", + "łyżka stołowa": "łyżkę stołową" + } + - PluralGenitive: { # Genitive plural - after 5 and above, and after 12-14, 112-114. # Polish inflects the noun according to the numeral, so a single plural diff --git a/tests/Languages/pl.rs b/tests/Languages/pl.rs index c9423ea9f..8e4c56f37 100644 --- a/tests/Languages/pl.rs +++ b/tests/Languages/pl.rs @@ -4,6 +4,7 @@ mod pl; mod number_sets; mod unit_plurals; +mod preposition_case; // Tests mirrored from the English suite, with expected speech localized to Polish. mod ClearSpeak { diff --git a/tests/Languages/pl/ClearSpeak/mfrac.rs b/tests/Languages/pl/ClearSpeak/mfrac.rs index a53bba559..fff485696 100644 --- a/tests/Languages/pl/ClearSpeak/mfrac.rs +++ b/tests/Languages/pl/ClearSpeak/mfrac.rs @@ -86,7 +86,7 @@ fn frac_with_units() -> Result<()> { "; - test("pl", "ClearSpeak", expr, "62 mile na godzina")?; + test("pl", "ClearSpeak", expr, "62 mile na godzinę")?; return Ok(()); } diff --git a/tests/Languages/pl/SimpleSpeak/mfrac.rs b/tests/Languages/pl/SimpleSpeak/mfrac.rs index a07f0592b..3b421b0f2 100644 --- a/tests/Languages/pl/SimpleSpeak/mfrac.rs +++ b/tests/Languages/pl/SimpleSpeak/mfrac.rs @@ -164,7 +164,7 @@ fn frac_with_units() -> Result<()> { "; - test("pl", "SimpleSpeak", expr, "62 mile na godzina")?; + test("pl", "SimpleSpeak", expr, "62 mile na godzinę")?; return Ok(()); } @@ -181,7 +181,7 @@ fn singular_frac_with_units() -> Result<()> { "; - test("pl", "SimpleSpeak", expr, "1 galon na mila")?; + test("pl", "SimpleSpeak", expr, "1 galon na milę")?; return Ok(()); } @@ -198,7 +198,7 @@ fn number_in_numerator_with_units() -> Result<()> { mi "; - test("pl", "SimpleSpeak", expr, "3 galony na mila")?; + test("pl", "SimpleSpeak", expr, "3 galony na milę")?; return Ok(()); } @@ -212,7 +212,7 @@ fn units_with_powers() -> Result<()> { s2 "; - test("pl", "SimpleSpeak", expr, "3 metry na sekunda do kwadratu")?; + test("pl", "SimpleSpeak", expr, "3 metry na sekundę do kwadratu")?; return Ok(()); } diff --git a/tests/Languages/pl/preposition_case.rs b/tests/Languages/pl/preposition_case.rs new file mode 100644 index 000000000..92d1a4521 --- /dev/null +++ b/tests/Languages/pl/preposition_case.rs @@ -0,0 +1,71 @@ +/// Polish prepositions govern case: the denominator of a unit fraction is spoken +/// after "na", which requires the accusative - "na godzinę", not "na godzina". +use crate::common::*; +use anyhow::Result; + +/// Feminine denominators: these are the ones whose form actually changes. +#[test] +fn per_fraction_feminine_denominator() -> Result<()> { + let expr = r#" + 62mihr, + 5ms, + 2kmhr, + 7radmin, + 3Jt + "#; + test("pl", "SimpleSpeak", expr, "62 mile na godzinę, przecinek; 5 metrów na sekundę, przecinek; 2 kilo-metry na godzinę; przecinek; 7 radianów na minutę, przecinek; 3 dżule na tonę")?; + Ok(()) +} + +/// Masculine and neuter denominators must NOT change: their accusative already +/// equals the nominative. This is the negative control for the change. +#[test] +fn per_fraction_masculine_denominator_unchanged() -> Result<()> { + let expr = r#" + 3gl, + 1Jmol, + 5Nm, + 2VA + "#; + test("pl", "SimpleSpeak", expr, "3 gramy na litr, przecinek; 1 dżul na mol, przecinek; 5 niutonów na metr, przecinek; 2 wolty na amper")?; + Ok(()) +} + +/// A unit standing on its own keeps the nominative - the accusative must not +/// leak outside the fraction denominator. +#[test] +fn standalone_unit_stays_nominative() -> Result<()> { + let expr = r#" + 1hr, + 1s, + 1t, + 1min + "#; + test("pl", "SimpleSpeak", expr, "1 godzina, przecinek; 1 sekunda, przecinek; 1 tona, przecinek; 1 minuta")?; + Ok(()) +} + +/// Multi-word feminine units: the adjective agrees, a dependent noun does not. +#[test] +fn per_fraction_multiword_denominator() -> Result<()> { + let expr = r#" + 3gsq ft, + 2marcmin + "#; + test("pl", "SimpleSpeak", expr, "3 gramy na stopę kwadratową; przecinek; 2 metry na minutę łuku")?; + Ok(()) +} + +/// A denominator raised to a power, as in m/s^2: the unit's parent is the +/// 'power' element, not the fraction, so this needs its own test. +#[test] +fn per_fraction_denominator_with_power() -> Result<()> { + let expr = r#" + + 3m + s2 + + "#; + test("pl", "SimpleSpeak", expr, "3 metry na sekundę do kwadratu")?; + Ok(()) +} From 6a5170a57526ea1c873f4cb67f92a0b892c6087f Mon Sep 17 00:00:00 2001 From: mgros Date: Fri, 4 Sep 2026 01:59:00 +0200 Subject: [PATCH 088/120] Update: Add `Cargo.lock` file to track dependencies --- .gitignore | 1 - Cargo.lock | 2075 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2075 insertions(+), 1 deletion(-) create mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index fef5d915c..b2784f7e8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ target build.rs.orig Python/libmathcat.pyd -Cargo.lock src-pest.7z src.7z mp2.7z diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..50bccf29f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,2075 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alsa" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" +dependencies = [ + "alsa-sys", + "bitflags 2.11.0", + "cfg-if", + "libc", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "android_log-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" + +[[package]] +name = "android_logger" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3" +dependencies = [ + "android_log-sys", + "env_filter 0.1.4", + "log", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse 0.2.7", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse 1.0.0", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "bzip2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" +dependencies = [ + "libbz2-rs-sys", +] + +[[package]] +name = "cc" +version = "1.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream 1.0.0", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2 1.0.106", + "quote 1.0.45", + "syn 2.0.117", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "coreaudio-rs" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aae284fbaf7d27aa0e292f7677dfbe26503b0d555026f702940805a630eac17" +dependencies = [ + "bitflags 1.3.2", + "libc", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", +] + +[[package]] +name = "cpal" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbd307f43cc2a697e2d1f8bc7a1d824b5269e052209e28883e5bc04d095aaa3f" +dependencies = [ + "alsa", + "coreaudio-rs", + "dasp_sample", + "jni", + "js-sys", + "libc", + "mach2", + "ndk", + "ndk-context", + "num-derive", + "num-traits", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2 1.0.106", + "quote 1.0.45", + "strsim", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core", + "quote 1.0.45", + "syn 2.0.117", +] + +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2 1.0.106", + "quote 1.0.45", + "syn 2.0.117", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.117", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.11.0", + "objc2", +] + +[[package]] +name = "doc-comment" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_filter" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a1c3cc8e57274ec99de65301228b537f1e4eedc1b8e0f9411c6caac8ae7308f" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" +dependencies = [ + "anstream 0.6.21", + "anstyle", + "env_filter 1.0.0", + "jiff", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "extended" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365" + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "miniz_oxide", + "zlib-rs", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hound" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" + +[[package]] +name = "html-escape" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" +dependencies = [ + "utf8-width", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "jiff" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c867c356cc096b33f4981825ab281ecba3db0acefe60329f044c1789d94c6543" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7946b4325269738f270bb55b3c19ab5c5040525f83fd625259422a9d25d9be5" +dependencies = [ + "proc-macro2 1.0.106", + "quote 1.0.45", + "syn 2.0.117", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote 1.0.45", + "syn 2.0.117", +] + +[[package]] +name = "js-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libbz2-rs-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "libredox" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +dependencies = [ + "bitflags 2.11.0", + "libc", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +dependencies = [ + "libc", +] + +[[package]] +name = "mathcat" +version = "0.7.6-rc.4" +dependencies = [ + "android_logger", + "anyhow", + "bitflags 2.11.0", + "cfg-if", + "clap", + "dirs", + "env_logger", + "fastrand", + "html-escape", + "log", + "natural-tts", + "phf", + "radix_fmt", + "regex", + "roman-numerals-rs", + "strum", + "sxd-document-no-unsafe", + "sxd-xpath-no-unsafe", + "tempfile", + "unicode-script", + "yaml-rust", + "zip", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "minreq" +version = "2.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05015102dad0f7d61691ca347e9d9d9006685a64aefb3d79eecf62665de2153d" +dependencies = [ + "rustls", + "rustls-webpki", + "webpki-roots", +] + +[[package]] +name = "natural-tts" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06ae31375ea7364dfadc29bbae70683fce13617e602c458898ebd2630fb1a89" +dependencies = [ + "derive_builder", + "hound", + "minreq", + "percent-encoding", + "rodio", + "serde", + "thiserror 2.0.18", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.11.0", + "jni-sys 0.3.1", + "log", + "ndk-sys", + "num_enum", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2 1.0.106", + "quote 1.0.45", + "syn 2.0.117", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate", + "proc-macro2 1.0.106", + "quote 1.0.45", + "syn 2.0.117", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-audio-toolbox" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08" +dependencies = [ + "bitflags 2.11.0", + "libc", + "objc2", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-audio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" +dependencies = [ + "dispatch2", + "objc2", + "objc2-core-audio-types", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-core-audio-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" +dependencies = [ + "bitflags 2.11.0", + "objc2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.11.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "objc2", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "peresil" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f658886ed52e196e850cfbbfddab9eaa7f6d90dd0929e264c31e5cec07e09e57" + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2 1.0.106", + "quote 1.0.45", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2 1.0.106", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "radix_fmt" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce082a9940a7ace2ad4a8b7d0b1eac6aa378895f18be598230c5f2284ac05426" + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rodio" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40ecf59e742e03336be6a3d53755e789fd05a059fa22dfa0ed624722319e183" +dependencies = [ + "cpal", + "dasp_sample", + "num-rational", + "symphonia", +] + +[[package]] +name = "roman-numerals-rs" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8408ef3512d2c5d6876de3cf8f24751e19c5f2566e37e1d31ced56a1892f1e92" + +[[package]] +name = "rustc-demangle" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2 1.0.106", + "quote 1.0.45", + "syn 2.0.117", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "snafu" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d0bf93d08d6a44363b47d737f1f5bebbf5e6a1eaaa3d4c128ceeaca6b718292" +dependencies = [ + "backtrace", + "doc-comment", + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624e94bd38e471f67883b467711e7a7ad7dbe284f5fb7e661dc8a671fc5b26a0" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2 1.0.106", + "quote 1.0.45", + "syn 2.0.117", +] + +[[package]] +name = "sxd-document-no-unsafe" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c08c6a37e27fb1559e036ae8f45672faf06623f13c74c8de9719d50536981f5" +dependencies = [ + "peresil", + "typed-arena", +] + +[[package]] +name = "sxd-xpath-no-unsafe" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff5bfe9c78786014d5a76d9328ea6508b0b74b7dc4a7c4d9b881ace23ce50d" +dependencies = [ + "peresil", + "snafu", + "sxd-document-no-unsafe", +] + +[[package]] +name = "symphonia" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5773a4c030a19d9bfaa090f49746ff35c75dfddfa700df7a5939d5e076a57039" +dependencies = [ + "lazy_static", + "symphonia-bundle-flac", + "symphonia-bundle-mp3", + "symphonia-codec-aac", + "symphonia-codec-pcm", + "symphonia-codec-vorbis", + "symphonia-core", + "symphonia-format-isomp4", + "symphonia-format-ogg", + "symphonia-format-riff", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-bundle-flac" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c91565e180aea25d9b80a910c546802526ffd0072d0b8974e3ebe59b686c9976" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-bundle-mp3" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4872dd6bb56bf5eac799e3e957aa1981086c3e613b27e0ac23b176054f7c57ed" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-codec-aac" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c263845aa86881416849c1729a54c7f55164f8b96111dba59de46849e73a790" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-codec-pcm" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e89d716c01541ad3ebe7c91ce4c8d38a7cf266a3f7b2f090b108fb0cb031d95" +dependencies = [ + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-codec-vorbis" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f025837c309cd69ffef572750b4a2257b59552c5399a5e49707cc5b1b85d1c73" +dependencies = [ + "log", + "symphonia-core", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-core" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea00cc4f79b7f6bb7ff87eddc065a1066f3a43fe1875979056672c9ef948c2af" +dependencies = [ + "arrayvec", + "bitflags 1.3.2", + "bytemuck", + "lazy_static", + "log", +] + +[[package]] +name = "symphonia-format-isomp4" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243739585d11f81daf8dac8d9f3d18cc7898f6c09a259675fc364b382c30e0a5" +dependencies = [ + "encoding_rs", + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-format-ogg" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b4955c67c1ed3aa8ae8428d04ca8397fbef6a19b2b051e73b5da8b1435639cb" +dependencies = [ + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + +[[package]] +name = "symphonia-format-riff" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d7c3df0e7d94efb68401d81906eae73c02b40d5ec1a141962c592d0f11a96f" +dependencies = [ + "extended", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-metadata" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36306ff42b9ffe6e5afc99d49e121e0bd62fe79b9db7b9681d48e29fa19e6b16" +dependencies = [ + "encoding_rs", + "lazy_static", + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-utils-xiph" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27c85ab799a338446b68eec77abf42e1a6f1bb490656e121c6e27bfbab9f16" +dependencies = [ + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" +dependencies = [ + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2 1.0.106", + "quote 1.0.45", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2 1.0.106", + "quote 1.0.45", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2 1.0.106", + "quote 1.0.45", + "syn 2.0.117", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + +[[package]] +name = "typed-path" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" + +[[package]] +name = "unicode-ident" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" + +[[package]] +name = "unicode-script" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "utf8-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" +dependencies = [ + "quote 1.0.45", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" +dependencies = [ + "bumpalo", + "proc-macro2 1.0.106", + "quote 1.0.45", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.25.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9252e5725dbed82865af151df558e754e4a3c2c30818359eb17465f1346a1b49" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12661b9c89351d684a50a8a643ce5f608e20243b9fb84687800163429f161d65" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "zip" +version = "8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" +dependencies = [ + "bzip2", + "crc32fast", + "flate2", + "indexmap", + "memchr", + "typed-path", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7948af682ccbc3342b6e9420e8c51c1fe5d7bf7756002b4a3c6cabfe96a7e3c" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] From a6b6af1592cf35c46a8d2309abdfd514c71e0115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Arborealis=20L=C3=B6tberg?= <143188129+TimArborealis@users.noreply.github.com> Date: Mon, 7 Sep 2026 13:34:39 +0200 Subject: [PATCH 089/120] Minor chemistry fix in Swedish Changed chemical arrow operator to read "reagerar och bildar" instead of "reaktion bildar", to make grammatical sense. --- Rules/Languages/sv/SharedRules/general.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rules/Languages/sv/SharedRules/general.yaml b/Rules/Languages/sv/SharedRules/general.yaml index 192c11204..48676832d 100644 --- a/Rules/Languages/sv/SharedRules/general.yaml +++ b/Rules/Languages/sv/SharedRules/general.yaml @@ -1161,7 +1161,7 @@ then_test: if: "$Verbosity='Terse'" then: [{T: "bildar"}] # phrase(hydrogen and oxygen 'forms' water ) - else: [{T: "reaktion bildar"}] # phrase(hydrogen and oxygen 'reacts to form' water) + else: [{T: "reagerar och bildar"}] # phrase(hydrogen and oxygen 'reacts to form' water) - else_if: ".='⇌' or .='\u1f8d2'" then: [{T: "är i jämvikt med"}] # phrase(a reactant 'is in equilibrium with' a product) - else_if: ".='\u1f8d4'" From bd03fcd02cc69344b574e1d417ded032e40ed290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dziwisz?= Date: Tue, 8 Sep 2026 00:57:22 +0200 Subject: [PATCH 090/120] pl: mark the unicode translations as verified (t -> T) (#741) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * pl: mark the unicode translations as verified (t -> T) Mechanical change: 2435 text keys raised from t/ot/ct to T/OT/CT in the two unicode files. No text is touched. The Polish rules were written before the lowercase/uppercase convention was in use, so entries that have been translated all along still carried the "needs review" key. The audit tool therefore reported them as untranslated, which made the real gaps impossible to see: audit-translations pl, untranslated text: 3162 -> 727 The remaining 727 are in the rule files, where the entries need reading rather than a mechanical pass, so they are deliberately left alone. Every raised entry was checked to differ from the English source, i.e. it really is translated. Entries whose text legitimately equals English (Roman numerals, ligatures, proper names, whitespace) were handled separately in the previous PR; entries still holding English text are NOT raised, since that would assert a translation that does not exist. Verification: cargo test --test languages Languages::pl 609 passed, 0 failed diff: 2436 insertions, 2436 deletions, zero changes outside key case The last point is checked by normalising the key case in the diff and confirming no unpaired lines remain (without normalising, 4710 remain, so the check does discriminate). * pl: also mark the multi-line unicode entries as verified The previous commit raised the key on entries written on a single line (`- "±": [t: "..."]`), but not on entries whose body is a block: - "°": - test: if: "parent::*[name(.)='msup' ...]" then: [t: "stopni"] else: [t: "stopień"] Those are exactly the entries carrying inflection or a TTS condition, so 312 of the most deliberate translations in the file were left looking unreviewed. This raises them: 64 in unicode.yaml, 247 in unicode-full.yaml and one single-line entry (U+23E6) missed earlier. One entry is deliberately left with a lower-case key: `t: ""` for U+00A0, the else-branch of the `@data-empty-in-2D` test. That is silence rather than text, and an upper-case key means "a translator checked this wording" - there is no wording to check. English keeps a lower-case key there too. No content changes; only key case. `cargo test --test languages Languages::pl` gives 609 passed, 0 failed, which is the same result as before this commit, as expected for a change the engine treats as equivalent (src/speech.rs matches "t"|"T" in one match arm). * pl: raise the remaining rule-file keys and test the number-set rules Two loose ends in the Polish localization. **Rule-file keys.** 248 entries in the rule files still used a lower-case key while carrying translated Polish text. Content is untouched - every one of the 248 changed lines differs from its predecessor only in the case of the key name, verified line by line after normalizing the key. 17 entries are deliberately left lower-case: five are the letters C, N, Q, R, Z in `dimension-number-sets` (the letter itself is the wording, as in English); `ct: "s"`/`ct: "-"` in the unit rule are the plural suffix and prefix separator; `navigate.yaml:176` is a Polish euphony variant ("ze" before "st-"); the remaining seven are XPath expressions rather than speech. **Tests for the number-set rules.** These rules had no Polish test at all, in a file where two silent defects were already found. New `tests/Languages/pl/number_sets.rs` covers the bare symbols, the dimension form, both sign variants, and ClearSpeak agreeing with SimpleSpeak. The `number_set_with_variable_exponent` case is the negative control: `dimension-number-sets` fires on `count(*)=2`, so a non-numeric exponent must fall through to the full phrase ("liczby całkowite do potęgi n") rather than being read as a dimension. Without it, a rule that swallowed every superscript would still pass. Polish keeps the letter with a dimension ("R 3"), matching en, de, fr, nb, sv, fi and es; Hungarian is the only language that translates in that position too. Measured with the SAPI voice NVDA actually uses: "R 3" is byte-identical to a phonetic "er 3" and "N 4" to "en 4", so the letters are pronounced as their Polish names. cargo test --test languages Languages::pl 614 passed, 0 failed --------- Co-authored-by: NSoiffer From e1990813b058ff4c27671c244c0960983e78acae Mon Sep 17 00:00:00 2001 From: "U-rainshowers\\neils" Date: Tue, 8 Sep 2026 12:26:42 +1000 Subject: [PATCH 091/120] add PauseFactor to test initialization so that local changes don't affect the outcome. Also consolodate nav preference testing. --- src/interface.rs | 22 ++++++++++++++++++++++ src/navigate.rs | 8 +------- tests/Languages/en/navigate.rs | 15 +-------------- tests/Languages/nb/navigate.rs | 15 +-------------- tests/Languages/ru/navigate.rs | 15 +-------------- tests/common/mod.rs | 11 +++++++++++ 6 files changed, 37 insertions(+), 49 deletions(-) diff --git a/src/interface.rs b/src/interface.rs index 3177c22c8..157a895f1 100644 --- a/src/interface.rs +++ b/src/interface.rs @@ -540,6 +540,28 @@ pub fn do_navigate_command(command: impl AsRef) -> Result { return report_any_panic(result); } +/// Preferences shared by navigation unit and integration tests. +/// +/// Sets `PauseFactor` to 100 so local user prefs (e.g. AppData) cannot change +/// TTS:None pause punctuation (`,` vs `;`) in goldens. +/// `auto_zoom_out` is true for most `src/navigate.rs` tests and false for +/// `tests/Languages/*/navigate.rs`. +pub fn set_navigation_test_preferences( + language: impl AsRef, + nav_mode: impl AsRef, + auto_zoom_out: bool, +) -> Result<()> { + set_preference("NavMode", nav_mode)?; + set_preference("NavVerbosity", "Verbose")?; + set_preference("AutoZoomOut", if auto_zoom_out { "True" } else { "False" })?; + set_preference("Language", language)?; + set_preference("SpeechStyle", "SimpleSpeak")?; + set_preference("Verbosity", "Medium")?; + set_preference("PauseFactor", "100")?; + set_preference("Overview", "False")?; + return Ok(()); +} + /// Given an 'id' and an offset (for tokens), set the navigation node to that id. /// An error is returned if the 'id' doesn't exist pub fn set_navigation_node(id: impl AsRef, offset: usize) -> Result<()> { diff --git a/src/navigate.rs b/src/navigate.rs index 1fac4e766..d1444a86c 100644 --- a/src/navigate.rs +++ b/src/navigate.rs @@ -1030,13 +1030,7 @@ mod tests { init_panic_handler(); let result = catch_unwind(AssertUnwindSafe(|| { set_rules_dir(super::super::abs_rules_dir_path())?; - set_preference("NavMode", nav_mode_default)?; - set_preference("NavVerbosity", "Verbose")?; - set_preference("AutoZoomOut", "True")?; - set_preference("Language", language)?; - set_preference("SpeechStyle", "SimpleSpeak")?; - set_preference("Verbosity", "Medium")?; - set_preference("Overview", "False")?; + set_navigation_test_preferences(language, nav_mode_default, true)?; set_mathml(mathml)?; return Ok( () ); })); diff --git a/tests/Languages/en/navigate.rs b/tests/Languages/en/navigate.rs index abb317f18..6752f9773 100644 --- a/tests/Languages/en/navigate.rs +++ b/tests/Languages/en/navigate.rs @@ -8,23 +8,10 @@ use crate::common::*; use anyhow::Result; use std::panic::{catch_unwind, AssertUnwindSafe}; -fn init_nav(mathml: &str) -> Result<()> { - set_rules_dir(abs_rules_dir_path())?; - set_preference("Language", "en")?; - 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(()) -} - fn assert_zoom_in(command: &str, mathml: &str, expected: &str) -> Result<()> { init_panic_handler(); let result = catch_unwind(AssertUnwindSafe(|| { - init_nav(mathml)?; + init_nav("en", mathml)?; let speech = do_navigate_command(command)?; let trimmed_speech = speech.trim_end_matches([' ', ',', ';']).to_string(); assert_eq!(expected, trimmed_speech); diff --git a/tests/Languages/nb/navigate.rs b/tests/Languages/nb/navigate.rs index 4f1b8dbf2..fd254c6b3 100644 --- a/tests/Languages/nb/navigate.rs +++ b/tests/Languages/nb/navigate.rs @@ -8,23 +8,10 @@ use crate::common::*; use anyhow::Result; use std::panic::{catch_unwind, AssertUnwindSafe}; -fn init_nav(mathml: &str) -> Result<()> { - set_rules_dir(abs_rules_dir_path())?; - set_preference("Language", "nb")?; - 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(()) -} - fn assert_zoom_in(command: &str, mathml: &str, expected: &str) -> Result<()> { init_panic_handler(); let result = catch_unwind(AssertUnwindSafe(|| { - init_nav(mathml)?; + init_nav("nb", mathml)?; let speech = do_navigate_command(command)?; let trimmed_speech = speech.trim_end_matches([' ', ',', ';']).to_string(); assert_eq!(expected, trimmed_speech); diff --git a/tests/Languages/ru/navigate.rs b/tests/Languages/ru/navigate.rs index 690df9eff..0195f5bde 100644 --- a/tests/Languages/ru/navigate.rs +++ b/tests/Languages/ru/navigate.rs @@ -4,23 +4,10 @@ use crate::common::*; use anyhow::Result; use std::panic::{catch_unwind, AssertUnwindSafe}; -fn init_nav(mathml: &str) -> Result<()> { - set_rules_dir(abs_rules_dir_path())?; - set_preference("Language", "ru")?; - 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(()) -} - fn assert_zoom_in(mathml: &str, expected: &str) -> Result<()> { init_panic_handler(); let result = catch_unwind(AssertUnwindSafe(|| { - init_nav(mathml)?; + init_nav("ru", mathml)?; let speech = do_navigate_command("ZoomIn")?; let trimmed_speech = speech.trim_end_matches([' ', ',', ';']).to_string(); assert_eq!(expected, trimmed_speech); diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 6b42a25b8..837fca05b 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -75,6 +75,17 @@ fn set_default_speech_prefs() { prefs.set_user_prefs("Impairment", "Blindness").unwrap(); }); } + +/// Initialize navigation prefs + MathML for `tests/Languages/*/navigate.rs`. +/// Uses the same prefs as `src/navigate.rs` tests via [`set_navigation_test_preferences`], +/// except `AutoZoomOut` is false (language ZoomIn speech tests need that). +#[allow(dead_code)] // used in testing +pub fn init_nav(language: &str, mathml: &str) -> Result<()> { + set_rules_dir(abs_rules_dir_path())?; + set_navigation_test_preferences(language, "Enhanced", false)?; + set_mathml(mathml)?; + Ok(()) +} // Compare the result of speaking the mathml input to the output 'speech' // This uses default preferences #[allow(dead_code)] // used in testing From d3b7970e38fa073e1f187afb6537e37d6b9ab859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Gro=C3=9F?= Date: Tue, 8 Sep 2026 05:59:07 +0200 Subject: [PATCH 092/120] Fix pica unit parsing in spacing width conversion (#755) --- src/xpath_functions.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/xpath_functions.rs b/src/xpath_functions.rs index 97e1db5ee..17b6d360b 100644 --- a/src/xpath_functions.rs +++ b/src/xpath_functions.rs @@ -1408,9 +1408,10 @@ pub struct FontSizeGuess; /// "" // returns original node match isn't found impl FontSizeGuess { + /// Returns an estimated width in em units, using an assumed 12-point font for conversions. pub fn em_from_value(value_with_unit: &str) -> f64 { // match one or more digits followed by a unit -- there are many more units, but they tend to be large and rarer(?) - static FONT_VALUE: LazyLock = LazyLock::new(|| { Regex::new(r"(-?[0-9]*\.?[0-9]*)(px|cm|mm|Q|in|ppc|pt|ex|em|rem)").unwrap() }); + static FONT_VALUE: LazyLock = LazyLock::new(|| { Regex::new(r"(-?[0-9]*\.?[0-9]*)(px|cm|mm|Q|in|pc|pt|ex|em|rem)").unwrap() }); let cap = FONT_VALUE.captures(value_with_unit); if let Some(cap) = cap { if cap.len() == 3 { @@ -1784,6 +1785,16 @@ mod tests { return Ok( () ); } + // Pica widths must use the existing 12pt font estimate: 1pc, 12pt, and 1em are equivalent. + #[test] + fn font_size_guess_pica_widths() { + assert_eq!(FontSizeGuess::em_from_value("1pc"), 1.0); + assert_eq!(FontSizeGuess::em_from_value("0.5pc"), 0.5); + assert_eq!(FontSizeGuess::em_from_value("-1pc"), -1.0); + assert_eq!(FontSizeGuess::em_from_value("12pt"), 1.0); + assert_eq!(FontSizeGuess::em_from_value("1em"), 1.0); + } + #[test] fn ordinal_one_digit() -> Result<()> { return xpath_test(|| { From 07149291fde5258e24148f18139082d49ebf2016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20Arborealis=20L=C3=B6tberg?= <143188129+TimArborealis@users.noreply.github.com> Date: Tue, 8 Sep 2026 09:34:46 +0200 Subject: [PATCH 093/120] Update chemistry.rs --- tests/Languages/sv/chemistry.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/Languages/sv/chemistry.rs b/tests/Languages/sv/chemistry.rs index 6332997fc..09612bd26 100644 --- a/tests/Languages/sv/chemistry.rs +++ b/tests/Languages/sv/chemistry.rs @@ -160,9 +160,9 @@ fn beta_decay() -> Result<()> { test_prefs("sv", "ClearSpeak", vec![("Verbosity", "Terse")], expr, "14, 6, versal c; bildar, 14, 7, versal n; plus 0, minus 1, e")?; test_prefs("sv", "ClearSpeak", vec![("Verbosity", "Medium")], expr, - "upphöjt 14, nedsänkt 6, versal c; reaktion bildar; upphöjt 14, nedsänkt 7, versal n; plus, upphöjt 0, nedsänkt minus 1, e")?; + "upphöjt 14, nedsänkt 6, versal c; reagerar och bildar; upphöjt 14, nedsänkt 7, versal n; plus, upphöjt 0, nedsänkt minus 1, e")?; test_prefs("sv", "ClearSpeak", vec![("Verbosity", "Verbose")], expr, - "övre index 14, nedre index 6, versal c; reaktion bildar; övre index 14, nedre index 7, versal n; plus, övre index 0, nedre index minus 1, e")?; + "övre index 14, nedre index 6, versal c; reagerar och bildar; övre index 14, nedre index 7, versal n; plus, övre index 0, nedre index minus 1, e")?; return Ok(()); } @@ -426,9 +426,9 @@ fn mhchem_beta_decay() -> Result<()> { test_prefs("sv", "ClearSpeak", vec![("Verbosity", "Terse")], expr, "14, 6, versal c; bildar, 14, 7, versal n; plus 0, minus 1, e")?; test_prefs("sv", "ClearSpeak", vec![("Verbosity", "Medium")], expr, - "upphöjt 14, nedsänkt 6, versal c; reaktion bildar; upphöjt 14, nedsänkt 7, versal n; plus, upphöjt 0, nedsänkt minus 1, e")?; + "upphöjt 14, nedsänkt 6, versal c; reagerar och bildar; upphöjt 14, nedsänkt 7, versal n; plus, upphöjt 0, nedsänkt minus 1, e")?; test_prefs("sv", "ClearSpeak", vec![("Verbosity", "Verbose")], expr, - "övre index 14, nedre index 6, versal c; reaktion bildar; övre index 14, nedre index 7, versal n; plus, övre index 0, nedre index minus 1, e")?; + "övre index 14, nedre index 6, versal c; reagerar och bildar; övre index 14, nedre index 7, versal n; plus, övre index 0, nedre index minus 1, e")?; return Ok(()); } @@ -443,7 +443,7 @@ fn hcl_na_yields() -> Result<()> { "; test_prefs("sv", "SimpleSpeak", vec![("Verbosity", "Verbose")], expr, - "2, versal h, versal c l; plus 2 versal n a; reaktion bildar; 2, versal n a, versal c l; plus versal h, nedre index 2")?; + "2, versal h, versal c l; plus 2 versal n a; reagerar och bildar; 2, versal n a, versal c l; plus versal h, nedre index 2")?; return Ok(()); } From d5d1933c07396b84642e6839eb8d3569340d499d Mon Sep 17 00:00:00 2001 From: Jeff Witt <1848307+wittjeff@users.noreply.github.com> Date: Sun, 13 Sep 2026 07:37:39 -0400 Subject: [PATCH 094/120] Fix "TEMP NAME of" when a parenthesized matrix is raised to a power (#762) (#763) * fix(speech): flatten nested TEMP_NAME wrappers so a matrix raised to a power is not read as "TEMP NAME of" The mrow 'matrix' intent rule replaces with a bare `x: "*[2]"`, so its result is wrapped in the internal TEMP_NAME transport element; the parent `power` rule's `x: "*[1]"` wraps it again and lift_children only stripped one level. The leftover wrapper reached the generic function-intent speech rule, which spoke its name: "TEMP NAME of the 2 by 2 matrix; ...; squared" (#762). lift_children now flattens nested wrappers recursively. A leaf wrapper holding text is still only unwrapped as a direct child, matching the previous behavior. Two regression tests added to tests/Languages/en/mtable.rs; no goldens changed. Fixes #762 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01U86v5m1m8oaLSy5UsNbBpc * Simplify condition in push_lifted function Fixes clippy warning * Fix indent and delete extra "}" I should have deleted on the last change --------- Co-authored-by: Claude Fable 5.1 Co-authored-by: NSoiffer --- src/speech.rs | 32 ++++++++++++++++++-------- tests/Languages/en/mtable.rs | 44 ++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/src/speech.rs b/src/speech.rs index 100d5ff39..544ce35c7 100644 --- a/src/speech.rs +++ b/src/speech.rs @@ -709,24 +709,36 @@ impl Intent { /// "lift" up the children any "TEMP_NAME" child -- could short circuit when only one child + /// + /// TEMP_NAME is only ever a transport wrapper created by `replace_nodes_tree`, so wrappers can nest: + /// a rule whose replacement is a bare `x:` (e.g., the mrow 'matrix' rule returns `x: "*[2]"`) yields + /// TEMP_NAME(matrix), and the parent intent's `x: "*[1]"` wraps that again. Lifting only one level left + /// `power(TEMP_NAME(matrix), 2)`, spoken as "TEMP NAME of the 2 by 2 matrix ... squared" (issue #762). + /// Nested wrappers whose children are all elements are flattened; a leaf wrapper (text from a Text or + /// Attribute node) is only unwrapped when it is a direct child, matching the previous behavior. fn lift_children(result: Element) -> Element { // debug!("lift_children:\n{}", mml_to_string(result)); // most likely there will be the same number of new children as result has, but there could be more let mut new_children = Vec::with_capacity(2*result.children().len()); for child_of_element in result.children() { - match child_of_element { - ChildOfElement::Element(child) => { - if name(child) == "TEMP_NAME" { - new_children.append(&mut child.children()); // almost always just one - } else { - new_children.push(child_of_element); - } - }, - _ => new_children.push(child_of_element), // text() - } + push_lifted(child_of_element, &mut new_children, true); } result.replace_children(new_children); return result; + + fn push_lifted<'a>(child_of_element: ChildOfElement<'a>, new_children: &mut Vec>, is_direct_child: bool) { + if let ChildOfElement::Element(child) = child_of_element && name(child) == "TEMP_NAME" { + let grandchildren = child.children(); + let is_leaf_wrapper = grandchildren.iter().any(|gc| matches!(gc, ChildOfElement::Text(_))); + if is_direct_child || !is_leaf_wrapper { + for grandchild in grandchildren { + push_lifted(grandchild, new_children, false); + } + return; + } + } + new_children.push(child_of_element); + } } } } diff --git a/tests/Languages/en/mtable.rs b/tests/Languages/en/mtable.rs index 8a670988e..e2d063689 100644 --- a/tests/Languages/en/mtable.rs +++ b/tests/Languages/en/mtable.rs @@ -1390,3 +1390,47 @@ fn single_line_with_label() -> Result<()> { expr, "1 equation, with label 2; b equals 2")?; return Ok(()); } + +#[test] +fn matrix_raised_to_power() -> Result<()> { + // A parenthesized matrix as the base of msup must speak as "the matrix ... squared". + // Regression for #762: the mrow 'matrix' intent rule returns a bare `x:`, so the intent tree + // carried a nested TEMP_NAME wrapper into `power` and NVDA read "TEMP NAME of the 2 by 2 matrix ...". + let expr = " + ( + + 12 + 34 + + ) + 2 + "; + test("en", "ClearSpeak", expr, "the 2 by 2 matrix; row 1; 1, 2; row 2; 3, 4; squared") +} + +#[test] +fn matrix_raised_to_power_lualatex_spacing() -> Result<()> { + // The exact MathML from #762 (LuaLaTeX pmatrix output): negative-width mspace around the + // table and fence attributes on the parens. Canonicalization must fold the spacing away so + // the matrix is still recognized as the base of the power. + let expr = " + + + ( + + + + + 12 + 34 + + + + + ) + + 2 + + "; + test("en", "ClearSpeak", expr, "the 2 by 2 matrix; row 1; 1, 2; row 2; 3, 4; squared") +} From 8ef6ee1b134262b7f6db24851ca2da2588052b82 Mon Sep 17 00:00:00 2001 From: "U-rainshowers\\neils" Date: Tue, 15 Sep 2026 10:59:32 +1000 Subject: [PATCH 095/120] Fix typo in fallback --- src/speech.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/speech.rs b/src/speech.rs index 544ce35c7..6d5b252a7 100644 --- a/src/speech.rs +++ b/src/speech.rs @@ -293,7 +293,7 @@ pub fn process_include(current_file: &Path, new_file_name: &str, mut read_new // get the subdir ...Rules/Braille/en/... // could have ...Rules/Braille/definitions.yaml, so 'next()' doesn't exist in this case, but the file wasn't zipped up if let Some(subdir) = new_file.strip_prefix(unzip_dir).unwrap().iter().next() { - let default_lang = if unzip_dir.ends_with("Languages") {"en"} else {"UEB;"}; + let default_lang = if unzip_dir.ends_with("Languages") {"en"} else {"UEB"}; PreferenceManager::unzip_files(unzip_dir, subdir.to_str().unwrap(), Some(default_lang)).unwrap_or_default(); } } @@ -2868,8 +2868,6 @@ pub fn braille_replace_chars(str: &str, mathml: Element) -> Result { ), Err(e) => Err(e), } - - }) } From 87f4c590a497acaa5daeb2b9864493760e3b0f15 Mon Sep 17 00:00:00 2001 From: "U-rainshowers\\neils" Date: Tue, 15 Sep 2026 11:01:03 +1000 Subject: [PATCH 096/120] The word "of" was hard-coded into intents. This moves its value to definitions.yaml. This also cleans up some really old code that tried to figure out if what was read was a vector or hashmap/hashset. Fixes #739. --- Rules/Languages/en/definitions.yaml | 7 +++ src/definitions.rs | 69 +++++++++++++++++------------ src/infer_intent.rs | 17 ++++--- 3 files changed, 60 insertions(+), 33 deletions(-) diff --git a/Rules/Languages/en/definitions.yaml b/Rules/Languages/en/definitions.yaml index c71e10b89..9a720e7f4 100644 --- a/Rules/Languages/en/definitions.yaml +++ b/Rules/Languages/en/definitions.yaml @@ -363,6 +363,13 @@ "chemistry-concentration": "function= ; concentration: concentration of: the concentration of; end concentration", } + # The connector word to say for functional 'IntentMappings' (in English, "of") + # This is likely the word used for U+2061 in unicode.yaml, but for technical reasons needs to be defined here + # vector of length=1 because scalars not supported +- FunctionApplicationWord: [ + "of", "foo" + ] + # Names of functions that in terse mode don't say "of" (or it's equivalent in other languages) - TerseFunctionNames: { "divergence", "del:gradient", "curl" diff --git a/src/definitions.rs b/src/definitions.rs index 489c25b61..b7c45fca9 100644 --- a/src/definitions.rs +++ b/src/definitions.rs @@ -141,9 +141,20 @@ pub fn read_definitions_file(use_speech_defs: bool) -> Result> { let file_path = pref_manager.get_definitions_file(use_speech_defs); let definitions = if use_speech_defs {&SPEECH_DEFINITIONS} else {&BRAILLE_DEFINITIONS}; definitions.with( |defs| defs.borrow_mut().name_to_var_mapping.clear() ); - let mut new_files = vec![file_path.to_path_buf()]; - let mut files_read = read_one_definitions_file(use_speech_defs, file_path).with_context(|| format!("in file '{}", file_path.to_string_lossy()))?; - new_files.append(&mut files_read); + let mut files_read = read_one_definitions_file(use_speech_defs, file_path) + .with_context(|| format!("in file '{}'", file_path.to_string_lossy()))?; + let mut seen: HashSet = HashSet::with_capacity(files_read.len()); + let mut new_files: Vec = Vec::with_capacity(files_read.len()); + for p in files_read.drain(..) { + let canon = match crate::shim_filesystem::canonicalize_shim(&p) { + Ok(c) => c, + Err(_) => p, + }; + let key = canon.to_string_lossy().to_string(); + if seen.insert(key) { + new_files.push(canon); + } + } // merge the contents of `TrigFunctions` into a set that contains all the function names (from `AdditionalFunctionNames`). return definitions.with(|defs| { @@ -228,39 +239,21 @@ fn build_values(definition: &Yaml, use_speech_defs: bool, path: &Path) -> Result return Ok( Some(crate::speech::process_include(path, include_file_name, do_include_fn)?) ); } - let result; - if def_name.starts_with("Numbers") || def_name.ends_with("_vec") { - result = Contains::Vec( Rc::new( RefCell::new( get_vec_values(value.as_vec().unwrap())? ) ) ); + let result = if let Some(vec) = value.as_vec() { + Contains::Vec( Rc::new( RefCell::new( get_vec_values(vec)? ) ) ) } else { - // match value.as_vec() { - // Some(vec) => { - // result = Contains::Set( Rc::new( RefCell::new( get_set_values(vec)? ) ) ); }, - // None => { - // let dict = value.as_hash().ok_or_else(|| anyhow!("definition list value '{}' is not an array or dictionary", yaml_to_type(value)))?; - // result = Contains::Map( Rc::new( RefCell::new( get_map_values(dict) - // .chain_err(||format!("while reading value '{}'", def_name))? ) ) ); - - // }, - // } let dict = value.as_hash().ok_or_else(|| anyhow!("definition list value '{}' is not an array or dictionary", yaml_to_type(value)))?; if dict.is_empty() { - result = Contains::Set( Rc::new( RefCell::new( HashSet::with_capacity(0) ) ) ); + Contains::Set( Rc::new( RefCell::new( HashSet::with_capacity(0) ) ) ) } else { // peak and see if this is a set or a map let (_, entry_value) = dict.iter().next().unwrap(); if entry_value.is_null() { - result = Contains::Set( Rc::new( RefCell::new( get_set_values(dict) - .with_context(||format!("while reading value '{def_name}'"))? ) ) ); + Contains::Set( Rc::new( RefCell::new( get_set_values(dict) + .with_context(||format!("while reading value '{def_name}'"))? ) ) ) } else { - // peak and see if this is a set or a map - let (_, entry_value) = dict.iter().next().unwrap(); - if entry_value.is_null() { - result = Contains::Set( Rc::new( RefCell::new( get_set_values(dict) - .with_context(||format!("while reading value '{def_name}'"))? ) ) ); - } else { - result = Contains::Map( Rc::new( RefCell::new( get_map_values(dict) - .with_context(||format!("while reading value '{def_name}'"))? ) ) ); - } + Contains::Map( Rc::new( RefCell::new( get_map_values(dict) + .with_context(||format!("while reading value '{def_name}'"))? ) ) ) } } }; @@ -369,6 +362,26 @@ mod tests { }); } + #[test] + fn test_vec_not_numbers() { + let function_application_word = r#"[FunctionApplicationWord: ["of"]]"#; + let defs_build_fn = |variable_def_list: &Yaml| { + for variable_def in variable_def_list.as_vec().unwrap() { + if let Err(e) = build_values(variable_def, true, Path::new("")) { + bail!("{}", crate::interface::errors_to_string(&e.context(format!("in file {:?}", function_application_word)))); + } + } + return Ok(vec![]); + }; + compile_rule(function_application_word, defs_build_fn).unwrap(); + SPEECH_DEFINITIONS.with(|defs| { + let defs = defs.borrow(); + let word = defs.get_vec("FunctionApplicationWord"); + assert!(word.is_some()); + assert_eq!(word.unwrap().as_slice(), ["of"]); + }); + } + #[test] fn test_hashmap() { let units = r#"[Units: {"A": "amp", "g": "gram", "m": "meter", "sec": "second"}]"#; diff --git a/src/infer_intent.rs b/src/infer_intent.rs index 4dc0b3d2e..81cbb9933 100644 --- a/src/infer_intent.rs +++ b/src/infer_intent.rs @@ -314,19 +314,26 @@ pub fn intent_function_has_arity_match(intent_name: &str, fixity: &str, arg_coun /// Spoken glue immediately before argument `arg_index` (1-based) when using an arity template. /// Empty when not in a matching arity template (caller uses separator/`of` path instead). pub fn intent_function_glue_before(intent_name: &str, fixity: &str, arg_index: usize, arg_count: usize) -> String { - let Some(after_eq) = intent_mapping_for_fixity(intent_name, fixity) else { return String::new(); }; if !intent_function_is_arity_mode(intent_name, fixity) { return String::new(); } - let Some(words) = intent_function_arity_glue(&after_eq, arg_count) else { return String::new(); }; - // words has arg_count-1 entries for args 1..arg_count-1; last arg is preceded by "of" if arg_index == arg_count { - return "of".to_string(); + return crate::definitions::SPEECH_DEFINITIONS.with(|definitions| { + let definitions = definitions.borrow(); + let word_ref = definitions.get_vec("FunctionApplicationWord"); + if let Some(word) = word_ref && !word.is_empty() { + return word[0].clone(); + } + return String::new(); + }) } + let Some(after_eq) = intent_mapping_for_fixity(intent_name, fixity) else { return String::new(); }; + let Some(words) = intent_function_arity_glue(&after_eq, arg_count) else { return String::new(); }; + // words has arg_count-1 entries for args 1..arg_count-1; last arg is preceded by "of" if arg_index >= 1 && arg_index < arg_count { return words[arg_index - 1].to_string(); } - String::new() + return String::new(); } /// Separator between args when not using an arity template. From a74a6e2d45f1f0ca454091d75d56b4335c9a8e88 Mon Sep 17 00:00:00 2001 From: "U-rainshowers\\neils" Date: Tue, 15 Sep 2026 11:16:18 +1000 Subject: [PATCH 097/120] remove value used in debugging --- Rules/Languages/en/definitions.yaml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Rules/Languages/en/definitions.yaml b/Rules/Languages/en/definitions.yaml index 9a720e7f4..bd3931549 100644 --- a/Rules/Languages/en/definitions.yaml +++ b/Rules/Languages/en/definitions.yaml @@ -366,9 +366,7 @@ # The connector word to say for functional 'IntentMappings' (in English, "of") # This is likely the word used for U+2061 in unicode.yaml, but for technical reasons needs to be defined here # vector of length=1 because scalars not supported -- FunctionApplicationWord: [ - "of", "foo" - ] +- FunctionApplicationWord: ["of"] # Names of functions that in terse mode don't say "of" (or it's equivalent in other languages) - TerseFunctionNames: { From 2622ee5b2ef2bc3294ee50b1a520e2d65e2f3f1f Mon Sep 17 00:00:00 2001 From: Danil <81031453+Kostenkov-2021@users.noreply.github.com> Date: Sat, 19 Sep 2026 11:19:58 +0300 Subject: [PATCH 098/120] Fix audit-ignore rule scoping Adjust the audit-translations parser so `# audit-ignore` markers immediately above a rule apply to that rule, while still supporting existing inline markers inside a rule block. This also clarifies the documented behavior and adds regression tests for leading, inline, and first-item ignore cases. --- PythonScripts/audit_translations/README.md | 3 +- PythonScripts/audit_translations/parsers.py | 17 +++++-- .../audit_translations/tests/test_parsers.py | 47 +++++++++++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/PythonScripts/audit_translations/README.md b/PythonScripts/audit_translations/README.md index 8aedf1265..fe391d49b 100644 --- a/PythonScripts/audit_translations/README.md +++ b/PythonScripts/audit_translations/README.md @@ -12,7 +12,8 @@ The tool analyzes rule files to detect the following issues: * **Rule Differences:** Structural changes (match expressions, conditions, variables, or test/replace layout) between the source and target translation. * **Definition Coverage:** Compares literal `definitions.yaml` entries by name and collection kind (`vector`, `set`, or `map`). -Add `# audit-ignore` to a rule block to suppress auditing that rule. +Add `# audit-ignore` on the line immediately before a rule to suppress auditing +that rule. Existing markers inside a rule block are also supported. --- diff --git a/PythonScripts/audit_translations/parsers.py b/PythonScripts/audit_translations/parsers.py index 11dfc5292..a96de8bef 100644 --- a/PythonScripts/audit_translations/parsers.py +++ b/PythonScripts/audit_translations/parsers.py @@ -149,13 +149,20 @@ def format_tag(tag_value: Any) -> str | None: def build_raw_blocks(lines: list[str], starts: list[int]) -> list[str]: - blocks = [] if not starts: - return blocks + return [] + + block_starts = starts.copy() for idx, start in enumerate(starts): - end = starts[idx + 1] if idx + 1 < len(starts) else len(lines) - blocks.append("\n".join(lines[start:end])) - return blocks + lower_bound = starts[idx - 1] + 1 if idx else 0 + while start > lower_bound and (not lines[start - 1].strip() or lines[start - 1].lstrip().startswith("#")): + start -= 1 + block_starts[idx] = start + + return [ + "\n".join(lines[start : block_starts[idx + 1] if idx + 1 < len(block_starts) else len(lines)]) + for idx, start in enumerate(block_starts) + ] def _extract_item_fields(item: Any, is_unicode: bool) -> tuple[str, str | None, str | None, Any] | None: diff --git a/PythonScripts/audit_translations/tests/test_parsers.py b/PythonScripts/audit_translations/tests/test_parsers.py index db7f31e1f..a8d1c36db 100644 --- a/PythonScripts/audit_translations/tests/test_parsers.py +++ b/PythonScripts/audit_translations/tests/test_parsers.py @@ -174,6 +174,53 @@ def test_detects_audit_ignore(self): rules = parse_rules_file(content, data) assert rules[0].audit_ignore + def test_assigns_leading_audit_ignore_to_following_rule(self): + """A marker directly above a rule suppresses that rule, not its neighbour.""" + content = """- name: first + tag: mo + match: "." + +# audit-ignore: the following placeholder intentionally differs +# from the source-language rule. +- name: second + tag: mi + match: "false()" +""" + yaml = YAML() + data = yaml.load(content) + rules = parse_rules_file(content, data) + assert not rules[0].audit_ignore + assert rules[1].audit_ignore + + def test_keeps_audit_ignore_inside_rule(self): + """Existing markers within a rule block remain supported.""" + content = """- name: first + tag: mo + # audit-ignore + match: "." + +- name: second + tag: mi + match: "x" +""" + yaml = YAML() + data = yaml.load(content) + rules = parse_rules_file(content, data) + assert rules[0].audit_ignore + assert not rules[1].audit_ignore + + def test_assigns_file_header_to_first_rule(self): + """A leading marker before the first item is included in its raw block.""" + content = """# audit-ignore +- name: first + tag: mo + match: "." +""" + yaml = YAML() + data = yaml.load(content) + rules = parse_rules_file(content, data) + assert rules[0].audit_ignore + def test_handles_array_tag(self): """Ensure handles array tag.""" content = """- name: multi-tag From a6e65c80560ff302a82b6e821cd8758ce0aace03 Mon Sep 17 00:00:00 2001 From: dtsengchromium Date: Sat, 19 Sep 2026 04:39:38 -0700 Subject: [PATCH 099/120] Add in-memory virtual filesystem support to shim_filesystem (#760) * Add in-memory virtual filesystem support to shim_filesystem * Fix clippy warnings across codebase and tests --- src/prefs.rs | 14 +-- src/shim_filesystem.rs | 105 ++++++++++++++++---- tests/Languages/pt/ClearSpeak/sets.rs | 48 ++++----- tests/Languages/pt/SimpleSpeak/functions.rs | 30 +++--- tests/Languages/pt/escuta.rs | 12 +-- tests/Languages/pt/intent.rs | 25 +++-- tests/Languages/pt/shared.rs | 36 +++---- tests/Languages/pt/units.rs | 36 +++---- 8 files changed, 180 insertions(+), 126 deletions(-) diff --git a/src/prefs.rs b/src/prefs.rs index 4dc51c4bc..89748119f 100644 --- a/src/prefs.rs +++ b/src/prefs.rs @@ -261,17 +261,9 @@ impl PreferenceManager { // Note: if current_dir() also fails, unwrap_or_default yields an empty PathBuf, // and the result may remain relative. #[cfg(not(feature = "include-zip"))] - let rules_dir = match rules_dir.canonicalize() { - Err(_e) => { - if rules_dir.is_absolute() { - rules_dir - } else { - std::env::current_dir() - .unwrap_or_default() - .join(&rules_dir) - } - }, - Ok(rules_dir) => rules_dir, + let rules_dir = match canonicalize_shim(&rules_dir) { + Err(e) => bail!("set_rules_dir: could not canonicalize path {}: {}", rules_dir.display(), e), + Ok(rules_dir) => rules_dir, }; self.set_rules_dir(&rules_dir)?; diff --git a/src/shim_filesystem.rs b/src/shim_filesystem.rs index 14c5b26fb..d539a6300 100644 --- a/src/shim_filesystem.rs +++ b/src/shim_filesystem.rs @@ -281,15 +281,81 @@ cfg_if! { crate::interface::set_rules_dir("Rules").unwrap(); // force reinitialization after the change } } else { + use std::collections::{HashMap, HashSet}; + use std::sync::RwLock; + + static IN_MEMORY_FILES: RwLock>> = RwLock::new(None); + static IN_MEMORY_DIRS: RwLock>> = RwLock::new(None); + + pub fn add_in_memory_file(path: &str, content: &str) { + { + let mut files = IN_MEMORY_FILES.write().unwrap(); + files.get_or_insert_with(HashMap::new).insert(path.to_string(), content.to_string()); + } + + let path_obj = Path::new(path); + { + let mut dirs = IN_MEMORY_DIRS.write().unwrap(); + let dirs = dirs.get_or_insert_with(HashSet::new); + for parent in path_obj.ancestors().skip(1) { + let parent_str = parent.to_str().unwrap_or_default(); + if !parent_str.is_empty() { + dirs.insert(parent_str.to_string()); + } + } + } + } + + pub fn canonicalize_shim(path: &Path) -> std::io::Result { + use std::ffi::OsStr; + let dot = OsStr::new("."); + let dot_dot = OsStr::new(".."); + let mut result = PathBuf::new(); + for part in path.iter() { + if dot == part { + continue; + } else if dot_dot == part { + result.pop(); + } else { + result.push(part); + } + } + return Ok(result); + } + + fn get_in_memory_key(path: &Path) -> String { + canonicalize_shim(path).unwrap_or_else(|_| path.to_path_buf()).to_str().unwrap_or_default().to_string() + } + pub fn is_file_shim(path: &Path) -> bool { + if IN_MEMORY_FILES.read().unwrap().as_ref().is_some_and(|f| f.contains_key(&get_in_memory_key(path))) { + return true; + } return path.is_file(); } pub fn is_dir_shim(path: &Path) -> bool { + if IN_MEMORY_DIRS.read().unwrap().as_ref().is_some_and(|d| d.contains(&get_in_memory_key(path))) { + return true; + } return path.is_dir(); } pub fn find_files_in_dir_that_ends_with_shim(dir: &Path, ending: &str) -> Vec { + let dir_name = get_in_memory_key(dir); + let mut answer = Vec::new(); + if let Some(files) = IN_MEMORY_FILES.read().unwrap().as_ref() { + for file_name in files.keys() { + if file_name.starts_with(&dir_name) && file_name.ends_with(ending) { + let name = Path::new(file_name).file_name().unwrap_or_default().to_str().unwrap_or_default(); + answer.push(name.to_string()); + } + } + } + if !answer.is_empty() { + return answer; + } + match dir.read_dir() { Err(_) => return vec![], // empty Ok(read_dir) => { @@ -340,27 +406,6 @@ cfg_if! { } } - /// Resolves the path to an absolute, canonical form using the OS. - /// If `canonicalize()` fails (e.g., ACCESS_DENIED in containers), falls back to: - /// - returning the path as-is if it is already absolute, - /// - prepending the current working directory if it is relative. - /// Note: the fallback does not resolve symlinks or normalize `..`/`.` segments. - pub fn canonicalize_shim(path: &Path) -> std::io::Result { - match path.canonicalize() { - Ok(p) => Ok(p), - Err(_) => { - if path.is_absolute() { - Ok(path.to_path_buf()) - } else { - // Prepend cwd to make the relative path absolute. - // unwrap_or_default yields an empty PathBuf if cwd is unavailable, - // in which case the returned path will still be relative. - Ok(std::env::current_dir().unwrap_or_default().join(path)) - } - } - } - } - // Tests extract language/braille zips in parallel. During extraction, individual YAML // files can briefly exist in a partially-written state (created/truncated before the // full contents are flushed). Guard both extraction and YAML reads with the same lock @@ -379,6 +424,10 @@ cfg_if! { } pub fn read_to_string_shim(path: &Path) -> Result { + if let Some(content) = IN_MEMORY_FILES.read().unwrap().as_ref().and_then(|f| f.get(&get_in_memory_key(path)).cloned()) { + return Ok(content); + } + let path = match path.canonicalize() { Ok(path) => path, Err(_) => path.to_path_buf(), @@ -490,4 +539,18 @@ mod tests { assert_eq!("test-value", read_to_string_shim(&existing_file).unwrap()); assert!(read_to_string_shim(&test_dir.path().join("does-not-exist.yaml")).is_err()); } + + #[test] + fn in_memory_filesystem_shim() { + add_in_memory_file("virtual/rules/Languages/en/test_Rules.yaml", "key: value"); + let file_path = Path::new("virtual/rules/Languages/en/test_Rules.yaml"); + let dir_path = Path::new("virtual/rules/Languages/en"); + + assert!(is_file_shim(file_path)); + assert!(is_dir_shim(dir_path)); + assert_eq!(read_to_string_shim(file_path).unwrap(), "key: value"); + + let found = find_files_in_dir_that_ends_with_shim(dir_path, "_Rules.yaml"); + assert_eq!(found, vec!["test_Rules.yaml".to_string()]); + } } diff --git a/tests/Languages/pt/ClearSpeak/sets.rs b/tests/Languages/pt/ClearSpeak/sets.rs index ff2ee45cd..87923aeac 100644 --- a/tests/Languages/pt/ClearSpeak/sets.rs +++ b/tests/Languages/pt/ClearSpeak/sets.rs @@ -1,27 +1,27 @@ -/// Testes de conjuntos e pertinência — pt-BR, ClearSpeak e SimpleSpeak. -/// -/// POR QUE ESTE ARQUIVO EXISTE -/// O par ∈ / ∊ falou "é membro de" com as preferências padrão durante duas -/// rodadas inteiras sem ninguém notar, porque o pt não tinha NENHUM teste de -/// pertinência — nem ClearSpeak, nem SimpleSpeak. O ∉ foi corrigido sozinho -/// numa rodada e os outros dois ficaram para trás. Ver ACHADOS 7.8. -/// -/// A invariante que este arquivo trava: **∈ (0x2208) e ∊ (0x220a) são -/// variantes de forma do mesmo símbolo e devem falar igual em todos os ramos** -/// — nas cinco preferências de ClearSpeak_SetMemberSymbol, dentro e fora de -/// um conjunto, nos dois estilos de fala. -/// -/// COMO AS EXPECTATIVAS FORAM ESCRITAS -/// Todas saíram da saída real do motor, nunca de tradução do inglês. O método -/// é o do cabeçalho de tests/Languages/pt/shared.rs: rodar, comparar `left` -/// com `right`, e se o gerado estiver certo em português, copiá-lo; se estiver -/// errado, consertar a REGRA, nunca o teste. -/// -/// Cada teste de pertinência fixa ClearSpeak_SetMemberSymbol explicitamente, -/// inclusive no caso 'Auto'. Isso é de propósito: as preferências de -/// ClearSpeak não são reinicializadas entre testes na mesma thread, então um -/// teste que dependesse do padrão implícito poderia passar ou falhar conforme -/// a ordem de execução. +//! Testes de conjuntos e pertinência — pt-BR, ClearSpeak e SimpleSpeak. +//! +//! POR QUE ESTE ARQUIVO EXISTE +//! O par ∈ / ∊ falou "é membro de" com as preferências padrão durante duas +//! rodadas inteiras sem ninguém notar, porque o pt não tinha NENHUM teste de +//! pertinência — nem ClearSpeak, nem SimpleSpeak. O ∉ foi corrigido sozinho +//! numa rodada e os outros dois ficaram para trás. Ver ACHADOS 7.8. +//! +//! A invariante que este arquivo trava: **∈ (0x2208) e ∊ (0x220a) são +//! variantes de forma do mesmo símbolo e devem falar igual em todos os ramos** +//! — nas cinco preferências de ClearSpeak_SetMemberSymbol, dentro e fora de +//! um conjunto, nos dois estilos de fala. +//! +//! COMO AS EXPECTATIVAS FORAM ESCRITAS +//! Todas saíram da saída real do motor, nunca de tradução do inglês. O método +//! é o do cabeçalho de tests/Languages/pt/shared.rs: rodar, comparar `left` +//! com `right`, e se o gerado estiver certo em português, copiá-lo; se estiver +//! errado, consertar a REGRA, nunca o teste. +//! +//! Cada teste de pertinência fixa ClearSpeak_SetMemberSymbol explicitamente, +//! inclusive no caso 'Auto'. Isso é de propósito: as preferências de +//! ClearSpeak não são reinicializadas entre testes na mesma thread, então um +//! teste que dependesse do padrão implícito poderia passar ou falhar conforme +//! a ordem de execução. use crate::common::*; use anyhow::Result; diff --git a/tests/Languages/pt/SimpleSpeak/functions.rs b/tests/Languages/pt/SimpleSpeak/functions.rs index a656c2da0..1429d1447 100644 --- a/tests/Languages/pt/SimpleSpeak/functions.rs +++ b/tests/Languages/pt/SimpleSpeak/functions.rs @@ -1,18 +1,18 @@ -/// TESTES DO IDIOMA pt — como reconciliar com as regras -/// -/// As strings esperadas abaixo foram escritas para refletir a intenção -/// das regras em Rules/Languages/pt. Vírgulas e ponto-e-vírgula marcam -/// PAUSAS inseridas pelo motor e podem divergir levemente na primeira -/// execução. Método do guia oficial de tradutores: -/// 1. rode `cargo test Languages::pt` -/// 2. para cada falha, compare `left` (esperado) com `right` (gerado) -/// 3. se o GERADO estiver correto em português, copie-o para o teste; -/// se estiver errado, conserte a REGRA, nunca o teste. -/// -/// ATENÇÃO ao histórico: o arquivo espanhol equivalente continha testes -/// que não refletiam as regras (ex.: esperava "logaritmo natural" com a -/// regra dizendo "natural log") e dano de busca-e-troca (ex.: "eigh"→"8"). -/// Não herde strings esperadas de es/ sem conferir. +//! TESTES DO IDIOMA pt — como reconciliar com as regras +//! +//! As strings esperadas abaixo foram escritas para refletir a intenção +//! das regras em Rules/Languages/pt. Vírgulas e ponto-e-vírgula marcam +//! PAUSAS inseridas pelo motor e podem divergir levemente na primeira +//! execução. Método do guia oficial de tradutores: +//! 1. rode `cargo test Languages::pt` +//! 2. para cada falha, compare `left` (esperado) com `right` (gerado) +//! 3. se o GERADO estiver correto em português, copie-o para o teste; +//! se estiver errado, conserte a REGRA, nunca o teste. +//! +//! ATENÇÃO ao histórico: o arquivo espanhol equivalente continha testes +//! que não refletiam as regras (ex.: esperava "logaritmo natural" com a +//! regra dizendo "natural log") e dano de busca-e-troca (ex.: "eigh"→"8"). +//! Não herde strings esperadas de es/ sem conferir. use crate::common::*; use anyhow::Result; diff --git a/tests/Languages/pt/escuta.rs b/tests/Languages/pt/escuta.rs index 812be28be..661cf4c85 100644 --- a/tests/Languages/pt/escuta.rs +++ b/tests/Languages/pt/escuta.rs @@ -2,12 +2,12 @@ use crate::common::*; fn falar(estilo: &str, verbosidade: &str, mathml: &str) -> String { set_rules_dir(abs_rules_dir_path()).unwrap(); - set_preference("Language", "pt".to_string()).unwrap(); - set_preference("SpeechStyle", estilo.to_string()).unwrap(); - set_preference("Verbosity", verbosidade.to_string()).unwrap(); - set_preference("SpeechOverrides_CapitalLetters", "".to_string()).unwrap(); - set_preference("ClearSpeak_SetMemberSymbol", "Auto".to_string()).unwrap(); - match set_mathml(mathml.to_string()) { + set_preference("Language", "pt").unwrap(); + set_preference("SpeechStyle", estilo).unwrap(); + set_preference("Verbosity", verbosidade).unwrap(); + set_preference("SpeechOverrides_CapitalLetters", "").unwrap(); + set_preference("ClearSpeak_SetMemberSymbol", "Auto").unwrap(); + match set_mathml(mathml) { Ok(_) => match get_spoken_text() { Ok(fala) => regex::Regex::new(r" +").unwrap().replace_all(&fala, " ").to_string(), Err(e) => format!("[ERRO ao falar: {}]", errors_to_string(&e)), diff --git a/tests/Languages/pt/intent.rs b/tests/Languages/pt/intent.rs index df164bc1b..cd43e0251 100644 --- a/tests/Languages/pt/intent.rs +++ b/tests/Languages/pt/intent.rs @@ -1,17 +1,16 @@ -/// TESTES DO IDIOMA pt — como reconciliar com as regras -/// -/// As strings esperadas abaixo foram escritas para refletir a intenção -/// das regras em Rules/Languages/pt. Vírgulas e ponto-e-vírgula marcam -/// PAUSAS inseridas pelo motor e podem divergir levemente na primeira -/// execução. Método do guia oficial de tradutores: -/// 1. rode `cargo test Languages::pt` -/// 2. para cada falha, compare `left` (esperado) com `right` (gerado) -/// 3. se o GERADO estiver correto em português, copie-o para o teste; -/// se estiver errado, conserte a REGRA, nunca o teste. -/// +//! TESTES DO IDIOMA pt — como reconciliar com as regras +//! +//! As strings esperadas abaixo foram escritas para refletir a intenção +//! das regras em Rules/Languages/pt. Vírgulas e ponto-e-vírgula marcam +//! PAUSAS inseridas pelo motor e podem divergir levemente na primeira +//! execução. Método do guia oficial de tradutores: +//! 1. rode `cargo test Languages::pt` +//! 2. para cada falha, compare `left` (esperado) com `right` (gerado) +//! 3. se o GERADO estiver correto em português, copie-o para o teste; +//! se estiver errado, conserte a REGRA, nunca o teste. -/// Tests for rules shared between various speech styles: -/// * this has tests focused on the various alphabets +// Tests for rules shared between various speech styles: +// * this has tests focused on the various alphabets use crate::common::*; use anyhow::Result; diff --git a/tests/Languages/pt/shared.rs b/tests/Languages/pt/shared.rs index cf860236b..50a28d49a 100644 --- a/tests/Languages/pt/shared.rs +++ b/tests/Languages/pt/shared.rs @@ -1,21 +1,21 @@ -/// TESTES DO IDIOMA pt — como reconciliar com as regras -/// -/// As strings esperadas abaixo foram escritas para refletir a intenção -/// das regras em Rules/Languages/pt. Vírgulas e ponto-e-vírgula marcam -/// PAUSAS inseridas pelo motor e podem divergir levemente na primeira -/// execução. Método do guia oficial de tradutores: -/// 1. rode `cargo test Languages::pt` -/// 2. para cada falha, compare `left` (esperado) com `right` (gerado) -/// 3. se o GERADO estiver correto em português, copie-o para o teste; -/// se estiver errado, conserte a REGRA, nunca o teste. -/// -/// ATENÇÃO ao histórico: o arquivo espanhol equivalente continha testes -/// que não refletiam as regras (ex.: esperava "logaritmo natural" com a -/// regra dizendo "natural log") e dano de busca-e-troca (ex.: "eigh"→"8"). -/// Não herde strings esperadas de es/ sem conferir. - -/// Tests for rules shared between various speech styles: -/// * modified var +//! TESTES DO IDIOMA pt — como reconciliar com as regras +//! +//! As strings esperadas abaixo foram escritas para refletir a intenção +//! das regras em Rules/Languages/pt. Vírgulas e ponto-e-vírgula marcam +//! PAUSAS inseridas pelo motor e podem divergir levemente na primeira +//! execução. Método do guia oficial de tradutores: +//! 1. rode `cargo test Languages::pt` +//! 2. para cada falha, compare `left` (esperado) com `right` (gerado) +//! 3. se o GERADO estiver correto em português, copie-o para o teste; +//! se estiver errado, conserte a REGRA, nunca o teste. +//! +//! ATENÇÃO ao histórico: o arquivo espanhol equivalente continha testes +//! que não refletiam as regras (ex.: esperava "logaritmo natural" com a +//! regra dizendo "natural log") e dano de busca-e-troca (ex.: "eigh"→"8"). +//! Não herde strings esperadas de es/ sem conferir. + +// Tests for rules shared between various speech styles: +// * modified var use crate::common::*; use anyhow::Result; diff --git a/tests/Languages/pt/units.rs b/tests/Languages/pt/units.rs index 47bd73056..cf0048f0d 100644 --- a/tests/Languages/pt/units.rs +++ b/tests/Languages/pt/units.rs @@ -1,21 +1,21 @@ -/// TESTES DO IDIOMA pt — como reconciliar com as regras -/// -/// As strings esperadas abaixo foram escritas para refletir a intenção -/// das regras em Rules/Languages/pt. Vírgulas e ponto-e-vírgula marcam -/// PAUSAS inseridas pelo motor e podem divergir levemente na primeira -/// execução. Método do guia oficial de tradutores: -/// 1. rode `cargo test Languages::pt` -/// 2. para cada falha, compare `left` (esperado) com `right` (gerado) -/// 3. se o GERADO estiver correto em português, copie-o para o teste; -/// se estiver errado, conserte a REGRA, nunca o teste. -/// -/// ATENÇÃO ao histórico: o arquivo espanhol equivalente continha testes -/// que não refletiam as regras (ex.: esperava "logaritmo natural" com a -/// regra dizendo "natural log") e dano de busca-e-troca (ex.: "eigh"→"8"). -/// Não herde strings esperadas de es/ sem conferir. - -/// Tests for rules shared between various speech styles: -/// * modified var +//! TESTES DO IDIOMA pt — como reconciliar com as regras +//! +//! As strings esperadas abaixo foram escritas para refletir a intenção +//! das regras em Rules/Languages/pt. Vírgulas e ponto-e-vírgula marcam +//! PAUSAS inseridas pelo motor e podem divergir levemente na primeira +//! execução. Método do guia oficial de tradutores: +//! 1. rode `cargo test Languages::pt` +//! 2. para cada falha, compare `left` (esperado) com `right` (gerado) +//! 3. se o GERADO estiver correto em português, copie-o para o teste; +//! se estiver errado, conserte a REGRA, nunca o teste. +//! +//! ATENÇÃO ao histórico: o arquivo espanhol equivalente continha testes +//! que não refletiam as regras (ex.: esperava "logaritmo natural" com a +//! regra dizendo "natural log") e dano de busca-e-troca (ex.: "eigh"→"8"). +//! Não herde strings esperadas de es/ sem conferir. + +// Tests for rules shared between various speech styles: +// * modified var use crate::common::*; use anyhow::Result; From 49de6d67c802f0f516cef11eef8f2528339f2a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Gro=C3=9F?= Date: Sat, 19 Sep 2026 13:45:17 +0200 Subject: [PATCH 100/120] Refactor: Simplify match return logic and improve string interpolation readability (#774) --- src/bin/package_rules.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/bin/package_rules.rs b/src/bin/package_rules.rs index 900af235f..971e95175 100644 --- a/src/bin/package_rules.rs +++ b/src/bin/package_rules.rs @@ -37,7 +37,7 @@ fn main() -> ExitCode { return ExitCode::from(1); } - match package_rules( + return match package_rules( &cli.source, &cli.output, cli.minimize, @@ -45,15 +45,15 @@ fn main() -> ExitCode { ) { Ok(minimized) => { let label = if cli.minimize { "minimized" } else { "standard" }; - println!("Created {} archive: {}", label, cli.output.display()); + println!("Created {label} archive: {}", cli.output.display()); if cli.minimize { - println!(" minimized YAML files: {}", minimized); + println!(" minimized YAML files: {minimized}"); } - return ExitCode::SUCCESS; + ExitCode::SUCCESS } Err(e) => { - eprintln!("error: {}", e); - return ExitCode::from(1); + eprintln!("error: {e}"); + ExitCode::from(1) } } } From 5c8117e99041f74aeb4f322593ec11d1e3766d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dziwisz?= Date: Sun, 20 Sep 2026 09:21:16 +0200 Subject: [PATCH 101/120] de, el: use $CommandOffset instead of string-length($Prefix) in navigate.yaml (#766) Ports ec36e057 to the two remaining languages that still cut the direction word out of $NavCommand with the length of the SPOKEN prefix. $NavCommand is always English (ZoomIn, MoveNext), while $Prefix is the translated word, so string-length($Prefix)+1 only lands on the right offset while the prefix happens to match the English stem. In de and el the prefixes are still the untranslated zoom/move/read/describe, so all 18 branches work today by coincidence; localizing those strings -- the obvious thing for a translator to do -- silently kills every direction word. This is a no-op right now, which is exactly why it is a good time to do it: substring($NavCommand, $CommandOffset) returns the same value as before for every one of the 15 navigation commands with the current prefixes. Verified on the engine, not only on paper: Languages::de is 209/209 both with and without this change. Greek has no tests at all (tests/Languages/el exists but is not declared in tests/languages.rs), so el rests on the structural check: offsets and the list of 9 branches are now identical to en. Refs #740. --- Rules/Languages/de/navigate.yaml | 26 +++++++++++++------------- Rules/Languages/el/navigate.yaml | 26 +++++++++++++------------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Rules/Languages/de/navigate.yaml b/Rules/Languages/de/navigate.yaml index 4ff42e67a..0b5ea83e8 100644 --- a/Rules/Languages/de/navigate.yaml +++ b/Rules/Languages/de/navigate.yaml @@ -54,37 +54,37 @@ else: - test: - if: "starts-with($NavCommand, 'Zoom')" - then: [set_variables: [Prefix: "'zoom'"]] # phrase('zoom' in to see more details) + then: [set_variables: [Prefix: "'zoom'", CommandOffset: "5"]] # phrase('zoom' in to see more details) - else_if: "starts-with($NavCommand, 'Move')" - then: [set_variables: [Prefix: "'move'"]] # phrase('move' to next entry in table) + then: [set_variables: [Prefix: "'move'", CommandOffset: "5"]] # phrase('move' to next entry in table) - else_if: "starts-with($NavCommand, 'Read')" - then: [set_variables: [Prefix: "'read'"]] # phrase('read' to next entry in table) + then: [set_variables: [Prefix: "'read'", CommandOffset: "5"]] # phrase('read' to next entry in table) - else_if: "starts-with($NavCommand, 'Describe')" - then: [set_variables: [Prefix: "'describe'"]] # phrase('describe' to next entry in table) + then: [set_variables: [Prefix: "'describe'", CommandOffset: "9"]] # phrase('describe' to next entry in table) - test: if: "$Prefix != ''" then: - x: "$Prefix" - test: - - if: "substring($NavCommand, string-length($Prefix)+1) = 'In'" + - if: "substring($NavCommand, $CommandOffset) = 'In'" then: [T: "in"] # phrase(zoom 'in' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'InAll'" + - 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: "den ganzen weg"] # phrase(zoom 'out all the way' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Out'" + - else_if: "substring($NavCommand, $CommandOffset) = 'Out'" then: [T: "raus"] # phrase(zoom 'out' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'OutAll'" + - 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: "den ganzen weg"] # phrase(zoom 'out all the way' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Next'" + - else_if: "substring($NavCommand, $CommandOffset) = 'Next'" then: [T: "rechten"] # phrase(move to the 'right') - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Previous'" + - else_if: "substring($NavCommand, $CommandOffset) = 'Previous'" then: [T: "linken"] # phrase(move to the 'left') - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Current'" + - else_if: "substring($NavCommand, $CommandOffset) = 'Current'" then: [T: "aktuelle"] # phrase(who is the 'current' president) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'LineStart'" + - else_if: "substring($NavCommand, $CommandOffset) = 'LineStart'" then: [T: "start der linie"] # phrase(move 'to start of line') - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'LineEnd'" + - else_if: "substring($NavCommand, $CommandOffset) = 'LineEnd'" then: [T: "bis zum ende der linie"] # phrase(move 'to end of line') - pause: "medium" - set_variables: [MatchCounter: "1"] diff --git a/Rules/Languages/el/navigate.yaml b/Rules/Languages/el/navigate.yaml index 2466efd8c..1cbfcc6f5 100644 --- a/Rules/Languages/el/navigate.yaml +++ b/Rules/Languages/el/navigate.yaml @@ -54,37 +54,37 @@ else: - test: - if: "starts-with($NavCommand, 'Zoom')" - then: [set_variables: [Prefix: "'zoom'"]] # phrase('zoom' in to see more details) + then: [set_variables: [Prefix: "'zoom'", CommandOffset: "5"]] # phrase('zoom' in to see more details) - else_if: "starts-with($NavCommand, 'Move')" - then: [set_variables: [Prefix: "'move'"]] # phrase('move' to next entry in table) + then: [set_variables: [Prefix: "'move'", CommandOffset: "5"]] # phrase('move' to next entry in table) - else_if: "starts-with($NavCommand, 'Read')" - then: [set_variables: [Prefix: "'read'"]] # phrase('read' to next entry in table) + then: [set_variables: [Prefix: "'read'", CommandOffset: "5"]] # phrase('read' to next entry in table) - else_if: "starts-with($NavCommand, 'Describe')" - then: [set_variables: [Prefix: "'describe'"]] # phrase('describe' to next entry in table) + then: [set_variables: [Prefix: "'describe'", CommandOffset: "9"]] # phrase('describe' to next entry in table) - test: if: "$Prefix != ''" then: - x: "$Prefix" - test: - - if: "substring($NavCommand, string-length($Prefix)+1) = 'In'" + - if: "substring($NavCommand, $CommandOffset) = 'In'" then: [t: "εντός"] # phrase(zoom 'in' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'InAll'" + - 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: "\uF8FEed πλήρως"] #theodora. check # phrase(zoom 'out all the way' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Out'" + - else_if: "substring($NavCommand, $CommandOffset) = 'Out'" then: [t: "εκτός"] # phrase(zoom 'out' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'OutAll'" + - 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: "\uF8FEed πλήρως"] #theodora. check # phrase(zoom 'out all the way' to see more details) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Next'" + - else_if: "substring($NavCommand, $CommandOffset) = 'Next'" then: [t: "δεξιά"] # phrase(move to the 'right') - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Previous'" + - else_if: "substring($NavCommand, $CommandOffset) = 'Previous'" then: [t: "αριστερά"] # phrase(move to the 'left') - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'Current'" + - else_if: "substring($NavCommand, $CommandOffset) = 'Current'" then: [t: "τρέχον"] # phrase(who is the 'current' president) - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'LineStart'" + - else_if: "substring($NavCommand, $CommandOffset) = 'LineStart'" then: [t: "στην αρχή της γραμμής"] # phrase(move 'to start of line') - - else_if: "substring($NavCommand, string-length($Prefix)+1) = 'LineEnd'" + - else_if: "substring($NavCommand, $CommandOffset) = 'LineEnd'" then: [t: "στο τέλος της γραμμής"] # phrase(move 'to end of line') - pause: "medium" - set_variables: [MatchCounter: "$MatchCounter + 1"] From 600180b4291f0f5a5797d443e84dfb41337d29d5 Mon Sep 17 00:00:00 2001 From: Danil <81031453+Kostenkov-2021@users.noreply.github.com> Date: Sun, 20 Sep 2026 10:41:20 +0300 Subject: [PATCH 102/120] Fix audit-ignore parsing around comments This change fixes how audit_translations YAML blocks are split when comments or blank lines appear around list items. It keeps introductory comments with the correct rule, preserves explicit audit-ignore markers inside a rule, and avoids incorrectly attaching comment-only lines to neighboring items. --- PythonScripts/audit_translations/README.md | 4 +- PythonScripts/audit_translations/parsers.py | 35 ++++++++++++--- .../audit_translations/tests/test_parsers.py | 44 +++++++++++++++++-- 3 files changed, 71 insertions(+), 12 deletions(-) diff --git a/PythonScripts/audit_translations/README.md b/PythonScripts/audit_translations/README.md index fe391d49b..241a500d2 100644 --- a/PythonScripts/audit_translations/README.md +++ b/PythonScripts/audit_translations/README.md @@ -12,8 +12,8 @@ The tool analyzes rule files to detect the following issues: * **Rule Differences:** Structural changes (match expressions, conditions, variables, or test/replace layout) between the source and target translation. * **Definition Coverage:** Compares literal `definitions.yaml` entries by name and collection kind (`vector`, `set`, or `map`). -Add `# audit-ignore` on the line immediately before a rule to suppress auditing -that rule. Existing markers inside a rule block are also supported. +After a blank line, add `# audit-ignore` immediately before a rule to suppress +auditing that rule. Existing markers inside a rule block are also supported. --- diff --git a/PythonScripts/audit_translations/parsers.py b/PythonScripts/audit_translations/parsers.py index a96de8bef..bcbdd0027 100644 --- a/PythonScripts/audit_translations/parsers.py +++ b/PythonScripts/audit_translations/parsers.py @@ -154,15 +154,36 @@ def build_raw_blocks(lines: list[str], starts: list[int]) -> list[str]: block_starts = starts.copy() for idx, start in enumerate(starts): - lower_bound = starts[idx - 1] + 1 if idx else 0 - while start > lower_bound and (not lines[start - 1].strip() or lines[start - 1].lstrip().startswith("#")): + if idx == 0: + while start > 0 and (not lines[start - 1].strip() or lines[start - 1].lstrip().startswith("#")): + start -= 1 + block_starts[idx] = start + continue + + lower_bound = starts[idx - 1] + 1 + dash_line = re.fullmatch(r"\s*-\s*(?:#.*)?", lines[start - 1]) if start > lower_bound else None + if dash_line: start -= 1 - block_starts[idx] = start + block_starts[idx] = start + continue - return [ - "\n".join(lines[start : block_starts[idx + 1] if idx + 1 < len(block_starts) else len(lines)]) - for idx, start in enumerate(block_starts) - ] + comment_start = start + while comment_start > lower_bound and lines[comment_start - 1].lstrip().startswith("#"): + comment_start -= 1 + + # A separating blank makes the comment run introductory text for this + # item. Without one, leave the run with the preceding rule. + if comment_start > lower_bound and not lines[comment_start - 1].strip(): + start = comment_start - 1 + while start > lower_bound and not lines[start - 1].strip(): + start -= 1 + block_starts[idx] = start + + blocks = [] + for idx, start in enumerate(block_starts): + end = block_starts[idx + 1] if idx + 1 < len(block_starts) else len(lines) + blocks.append("\n".join(lines[start:end])) + return blocks def _extract_item_fields(item: Any, is_unicode: bool) -> tuple[str, str | None, str | None, Any] | None: diff --git a/PythonScripts/audit_translations/tests/test_parsers.py b/PythonScripts/audit_translations/tests/test_parsers.py index a8d1c36db..6714f3581 100644 --- a/PythonScripts/audit_translations/tests/test_parsers.py +++ b/PythonScripts/audit_translations/tests/test_parsers.py @@ -176,7 +176,8 @@ def test_detects_audit_ignore(self): def test_assigns_leading_audit_ignore_to_following_rule(self): """A marker directly above a rule suppresses that rule, not its neighbour.""" - content = """- name: first + content = """ +- name: first tag: mo match: "." @@ -194,7 +195,8 @@ def test_assigns_leading_audit_ignore_to_following_rule(self): def test_keeps_audit_ignore_inside_rule(self): """Existing markers within a rule block remain supported.""" - content = """- name: first + content = """ +- name: first tag: mo # audit-ignore match: "." @@ -209,9 +211,45 @@ def test_keeps_audit_ignore_inside_rule(self): assert rules[0].audit_ignore assert not rules[1].audit_ignore + def test_keeps_touching_comment_with_preceding_rule(self): + """A comment without a separating blank remains part of the preceding rule.""" + content = """ +- name: first + tag: mo + match: "." +# audit-ignore: this comment documents the first rule +- name: second + tag: mi + match: "x" +""" + yaml = YAML() + data = yaml.load(content) + rules = parse_rules_file(content, data) + assert rules[0].audit_ignore + assert not rules[1].audit_ignore + + def test_includes_comment_on_item_dash_line(self): + """A comment attached to an item's dash belongs to that item.""" + content = """ +- name: first + tag: mo + match: "." + +- # second rule introduction + name: second + tag: mi + match: "x" +""" + yaml = YAML() + data = yaml.load(content) + rules = parse_rules_file(content, data) + assert "second rule introduction" not in rules[0].raw_content + assert "second rule introduction" in rules[1].raw_content + def test_assigns_file_header_to_first_rule(self): """A leading marker before the first item is included in its raw block.""" - content = """# audit-ignore + content = """ +# audit-ignore - name: first tag: mo match: "." From 7f3e79dc19a6a8d3fca0732f60ff878174fe3a37 Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 01:23:11 +0200 Subject: [PATCH 103/120] add tests for intervals in German --- tests/Languages/de.rs | 1 + tests/Languages/de/ClearSpeak/intervals.rs | 149 +++++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 tests/Languages/de/ClearSpeak/intervals.rs diff --git a/tests/Languages/de.rs b/tests/Languages/de.rs index 4d2be48ab..b2c36e61e 100644 --- a/tests/Languages/de.rs +++ b/tests/Languages/de.rs @@ -2,6 +2,7 @@ mod ClearSpeak { mod functions; + mod intervals; mod large_ops; mod menclose; mod mfrac; diff --git a/tests/Languages/de/ClearSpeak/intervals.rs b/tests/Languages/de/ClearSpeak/intervals.rs new file mode 100644 index 000000000..3bf359a55 --- /dev/null +++ b/tests/Languages/de/ClearSpeak/intervals.rs @@ -0,0 +1,149 @@ +//! Regression tests for German ClearSpeak interval endpoint wording. +//! Each endpoint combination should use one German connector and no untranslated words. + +use crate::common::*; +use anyhow::Result; + +#[test] +fn interval_open_open() -> Result<()> { + let expr = " + ( + c,d + ) + "; + test_ClearSpeak( + "de", + "ClearSpeak_Paren", + "Interval", + expr, + "das Intervall c bis d, nicht einschließlich c und nicht einschließlich d", + ) +} + +#[test] +fn interval_open_closed() -> Result<()> { + let expr = " + ( + c,d + ] + "; + test_ClearSpeak( + "de", + "ClearSpeak_Paren", + "Interval", + expr, + "das Intervall c bis d, nicht einschließlich c aber einschließlich d", + ) +} + +#[test] +fn interval_closed_open() -> Result<()> { + let expr = " + [ + c,d + ) + "; + test_ClearSpeak( + "de", + "ClearSpeak_Paren", + "Interval", + expr, + "das Intervall c bis d, einschließlich c aber nicht einschließlich d", + ) +} + +#[test] +fn interval_closed_closed() -> Result<()> { + let expr = " + [ + c,d + ] + "; + test_ClearSpeak( + "de", + "ClearSpeak_Paren", + "Interval", + expr, + "das Intervall c bis d, einschließlich c und d", + ) +} + +#[test] +fn interval_negative_infinity_to_open_endpoint() -> Result<()> { + let expr = " + ( + -∞,d + ) + "; + test_ClearSpeak( + "de", + "ClearSpeak_Paren", + "Interval", + expr, + "das Intervall negative unendlich bis d, nicht einschließlich d", + ) +} + +#[test] +fn interval_negative_infinity_to_closed_endpoint() -> Result<()> { + let expr = " + ( + -∞,d + ] + "; + test_ClearSpeak( + "de", + "ClearSpeak_Paren", + "Interval", + expr, + "das Intervall negative unendlich bis d, einschließlich d", + ) +} + +#[test] +fn interval_open_endpoint_to_infinity() -> Result<()> { + let expr = " + ( + c,∞ + ) + "; + test_ClearSpeak( + "de", + "ClearSpeak_Paren", + "Interval", + expr, + "das Intervall c bis unendlich, nicht einschließlich c", + ) +} + +#[test] +fn interval_closed_endpoint_to_infinity() -> Result<()> { + let expr = " + [ + c,∞ + ) + "; + test_ClearSpeak( + "de", + "ClearSpeak_Paren", + "Interval", + expr, + "das Intervall c bis unendlich, einschließlich c", + ) +} + +#[test] +fn interval_between_infinities_has_no_endpoint_qualifier() -> Result<()> { + let expr = " + ( + -∞,∞ + ) + "; + test_ClearSpeak( + "de", + "ClearSpeak_Paren", + "Interval", + expr, + "das Intervall negative unendlich bis unendlich", + ) +} From f1895d960dcd692cc3c748564aaddc12025cd067 Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 01:41:01 +0200 Subject: [PATCH 104/120] Refine German interval descriptions and update translation rules --- Rules/Languages/de/ClearSpeak_Rules.yaml | 57 ++++++++++++++---------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/Rules/Languages/de/ClearSpeak_Rules.yaml b/Rules/Languages/de/ClearSpeak_Rules.yaml index 4c8f1d277..8d23c3502 100644 --- a/Rules/Languages/de/ClearSpeak_Rules.yaml +++ b/Rules/Languages/de/ClearSpeak_Rules.yaml @@ -532,37 +532,46 @@ - x: "*[2]" - pause: short - test: - if: "not($is_intervals_start_infinity)" + if: "not($is_intervals_start_infinity) and starts-with(name(.), 'open')" + then: + - T: "nicht einschließlich" + - x: "*[1]" + - test: + if: "not($is_intervals_start_infinity) and not(starts-with(name(.), 'open'))" then: - - test: - if: "starts-with(name(.), 'open')" - then: [T: "no"] - T: "einschließlich" - 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: "ohne"] - - else_if: "name(.)='closed-interval'" - then: [T: "mit"] - else: [T: "ohne"] - # some ugly logic dealing with connectives: or, but, but, and (cleaner to be part of next clause?) - - test: - if: not($is_intervals_end_infinity) + if: "not($is_intervals_start_infinity or $is_intervals_end_infinity) and name(.)='open-interval'" 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: "ohne"] - - T: "einschließlich" + - T: "und nicht einschließlich" + - x: "*[2]" + - test: + if: "not($is_intervals_start_infinity or $is_intervals_end_infinity) and name(.)='open-closed-interval'" + then: + - T: "aber einschließlich" + - x: "*[2]" + - test: + if: "not($is_intervals_start_infinity or $is_intervals_end_infinity) and name(.)='closed-open-interval'" + then: + - T: "aber nicht einschließlich" + - x: "*[2]" + - test: + if: "not($is_intervals_start_infinity or $is_intervals_end_infinity) and name(.)='closed-interval'" + then: + - T: "und" + - x: "*[2]" + - test: + if: "$is_intervals_start_infinity and not($is_intervals_end_infinity) and (name(.)='open-interval' or name(.)='closed-open-interval')" + then: + - T: "nicht einschließlich" + - x: "*[2]" + - test: + if: "$is_intervals_start_infinity and not($is_intervals_end_infinity) and (name(.)='open-closed-interval' or name(.)='closed-interval')" + then: + - T: "einschließlich" - x: "*[2]" - # onto the [not] [including]... part - name: binomial-frac-vector tag: matrix match: From ee539f2855b4e90a4fea5cb607f0c38f3480f790 Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 01:56:01 +0200 Subject: [PATCH 105/120] Add German ClearSpeak rule and tests for pause intent --- Rules/Languages/de/ClearSpeak_Rules.yaml | 15 +++++++++++++++ tests/Languages/de/intent.rs | 21 +++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/Rules/Languages/de/ClearSpeak_Rules.yaml b/Rules/Languages/de/ClearSpeak_Rules.yaml index 8d23c3502..d2551ffef 100644 --- a/Rules/Languages/de/ClearSpeak_Rules.yaml +++ b/Rules/Languages/de/ClearSpeak_Rules.yaml @@ -1,4 +1,19 @@ --- +- 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:')" diff --git a/tests/Languages/de/intent.rs b/tests/Languages/de/intent.rs index a93e49d2d..62e711db6 100644 --- a/tests/Languages/de/intent.rs +++ b/tests/Languages/de/intent.rs @@ -3,6 +3,27 @@ use crate::common::*; use anyhow::Result; +#[test] +fn clear_speak_short_pause_intent() -> Result<()> { + // A short pause intent should survive intent inference and render as a comma. + let expr = "xy"; + test("de", "ClearSpeak", expr, "x, y") +} + +#[test] +fn clear_speak_medium_pause_intent() -> Result<()> { + // An unsuffixed pause intent requests the default medium pause. + let expr = "xy"; + test("de", "ClearSpeak", expr, "x; y") +} + +#[test] +fn clear_speak_long_pause_intent() -> Result<()> { + // A long pause intent should be preserved in ClearSpeak text output. + let expr = "xy"; + test("de", "ClearSpeak", expr, "x; y") +} + #[test] fn silent_intent() -> Result<()> { From 21fd1ebf5358a03a00d5fafeb3e4d6f6b82f2248 Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 02:03:52 +0200 Subject: [PATCH 106/120] Add German localization for currency symbol placement and corresponding tests --- Rules/Languages/de/SharedRules/general.yaml | 18 +++++++++++++++++- tests/Languages/de/units.rs | 9 +++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Rules/Languages/de/SharedRules/general.yaml b/Rules/Languages/de/SharedRules/general.yaml index a5792c745..4c0f252fb 100644 --- a/Rules/Languages/de/SharedRules/general.yaml +++ b/Rules/Languages/de/SharedRules/general.yaml @@ -317,7 +317,23 @@ then: [x: "concat('\uF8FE', DefinitionValue($Word, 'Speech', 'PluralForms'))", ct: " "] else: [x: "concat('\uF8FE', $Word)", ct: "s"] - +# Currency symbols precede amounts in MathML but are spoken after them in German: $ 3 -> 3 dollar. +- name: currency + tag: mrow + match: "count(*)=3 and DefinitionValue(*[1], 'Speech', 'CurrencySymbols') != ''" + variables: + - 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: "$CurrencyWord"] + - name: sin tag: mi match: ".='sin'" diff --git a/tests/Languages/de/units.rs b/tests/Languages/de/units.rs index fffd6ac5a..81467b92f 100644 --- a/tests/Languages/de/units.rs +++ b/tests/Languages/de/units.rs @@ -103,6 +103,15 @@ fn non_si_unit_plural_regression() -> Result<()> { Ok(()) } +#[test] +fn currency_symbol_precedes_amount() -> Result<()> { + // German speaks a prefixed currency symbol after its amount. + let expr = r#"$3"#; + test("de", "ClearSpeak", expr, "3 dollar")?; + test("de", "SimpleSpeak", expr, "3 dollar")?; + Ok(()) +} + /* #[test] fn si_base_with_prefixes() -> Result<()> { From 327d988b3dfc7d0e7f4b6f51601b5a0d940e723e Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 02:05:57 +0200 Subject: [PATCH 107/120] add another test for euro --- tests/Languages/de/units.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Languages/de/units.rs b/tests/Languages/de/units.rs index 81467b92f..1c932ae1c 100644 --- a/tests/Languages/de/units.rs +++ b/tests/Languages/de/units.rs @@ -109,6 +109,10 @@ fn currency_symbol_precedes_amount() -> Result<()> { let expr = r#"$3"#; test("de", "ClearSpeak", expr, "3 dollar")?; test("de", "SimpleSpeak", expr, "3 dollar")?; + + let expr = r#"€1"#; + test("de", "ClearSpeak", expr, "1 euro")?; + test("de", "SimpleSpeak", expr, "1 euro")?; Ok(()) } From a4083cef4adf0dd8c73b1b04c3f46a4c5517458e Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 02:07:32 +0200 Subject: [PATCH 108/120] add another test for pound/pfund --- tests/Languages/de/units.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Languages/de/units.rs b/tests/Languages/de/units.rs index 1c932ae1c..605ac2ed1 100644 --- a/tests/Languages/de/units.rs +++ b/tests/Languages/de/units.rs @@ -113,6 +113,10 @@ fn currency_symbol_precedes_amount() -> Result<()> { let expr = r#"€1"#; test("de", "ClearSpeak", expr, "1 euro")?; test("de", "SimpleSpeak", expr, "1 euro")?; + + let expr = r#"£4,7"#; + test("de", "ClearSpeak", expr, "4,7 pfund")?; + test("de", "SimpleSpeak", expr, "4,7 pfund")?; Ok(()) } From 692343a4b3c97ae07682c300b6198e8ac48e06be Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 02:21:32 +0200 Subject: [PATCH 109/120] Update German ClearSpeak rules: Add support for principal logarithm (Log) and corresponding tests --- Rules/Languages/de/SharedRules/general.yaml | 6 ++++-- tests/Languages/de/ClearSpeak/functions.rs | 9 +++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Rules/Languages/de/SharedRules/general.yaml b/Rules/Languages/de/SharedRules/general.yaml index 4c0f252fb..3098d76ac 100644 --- a/Rules/Languages/de/SharedRules/general.yaml +++ b/Rules/Languages/de/SharedRules/general.yaml @@ -460,9 +460,9 @@ else: [T: "kovarianz"] # phrase('covariance' function) - - name: log # handle both log and ln (if in an mrow, 'intents' are used) + name: log # handle log, principal Log, and ln (if in an mrow, 'intents' are used) tag: mi - match: ".='log' or .='ln'" + match: ".='log' or .='Log' or .='ln'" replace: - bookmark: "@id" - test: @@ -472,6 +472,8 @@ - test: - if: ".= 'log'" then: [T: "log"] # phrase(the 'log' function is used in mathematics) + - else_if: ".= 'Log'" + then: [T: "hauptwert des logarithmus"] - else_if: "$Verbosity='Terse'" then: [spell: "'ln'"] else: [T: "natürliche logarithmus"] # phrase(the 'natural log' function is used in mathematics) diff --git a/tests/Languages/de/ClearSpeak/functions.rs b/tests/Languages/de/ClearSpeak/functions.rs index 6cb30e259..90217658e 100644 --- a/tests/Languages/de/ClearSpeak/functions.rs +++ b/tests/Languages/de/ClearSpeak/functions.rs @@ -106,6 +106,15 @@ fn simple_log() -> Result<()> { } +#[test] +fn principal_value_log() -> Result<()> { + // Uppercase Log denotes the principal-value logarithm. + let expr = "Logx"; + test("de", "ClearSpeak", expr, "der hauptwert des logarithmus, von x")?; + test("de", "SimpleSpeak", expr, "der hauptwert des logarithmus, von x")?; + Ok(()) +} + #[test] fn normal_log() -> Result<()> { let expr = "log(x+y)"; From 25059ddcd19d85abd4f7ac35eaba805cf504cbd5 Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 02:29:39 +0200 Subject: [PATCH 110/120] Refine German ClearSpeak fraction translations and update ordinal fraction definitions --- Rules/Languages/de/definitions.yaml | 4 +++- tests/Languages/de/ClearSpeak/mfrac.rs | 16 ++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Rules/Languages/de/definitions.yaml b/Rules/Languages/de/definitions.yaml index 94ac82330..de78c7530 100644 --- a/Rules/Languages/de/definitions.yaml +++ b/Rules/Languages/de/definitions.yaml @@ -253,7 +253,9 @@ # stop when regularity begins - NumbersOrdinalFractionalPluralOnes: [ - "nulle", "erste", "hälften" + "nulle", "erste", "hälften", "Drittel", "Viertel", "Fünftel", "Sechstel", "Siebtel", "Achtel", "Neuntel", "Zehntel", + "Elftel", "Zwölftel", "Dreizehntel", "Vierzehntel", "Fünfzehntel", "Sechzehntel", + "Siebzehntel", "Achtzehntel", "Neunzehntel" ] # What to use for multiples of 10 diff --git a/tests/Languages/de/ClearSpeak/mfrac.rs b/tests/Languages/de/ClearSpeak/mfrac.rs index 0081e0197..c5744d8aa 100644 --- a/tests/Languages/de/ClearSpeak/mfrac.rs +++ b/tests/Languages/de/ClearSpeak/mfrac.rs @@ -19,7 +19,7 @@ fn common_fraction_thirds() -> Result<()> { let expr = " 2 3 "; - test("de", "ClearSpeak", expr, "2 dritte")?; + test("de", "ClearSpeak", expr, "2 Drittel")?; return Ok(()); } @@ -29,8 +29,8 @@ fn common_fraction_tenths() -> Result<()> { let expr = " 17 10 "; - test_prefs("de", "ClearSpeak", vec![("Verbosity", "Medium"), ("ClearSpeak_Fractions", "Auto")], expr, "17 zehnte")?; - test_prefs("de", "ClearSpeak", vec![("Verbosity", "Medium"), ("ClearSpeak_Fractions", "Ordinal")], expr, "17 zehnte")?; + test_prefs("de", "ClearSpeak", vec![("Verbosity", "Medium"), ("ClearSpeak_Fractions", "Auto")], expr, "17 Zehntel")?; + test_prefs("de", "ClearSpeak", vec![("Verbosity", "Medium"), ("ClearSpeak_Fractions", "Ordinal")], expr, "17 Zehntel")?; return Ok(()); } @@ -42,7 +42,7 @@ fn not_ClearSpeak_common_fraction_tenths() -> Result<()> { 89 10 "; test_prefs("de", "ClearSpeak", vec![("Verbosity", "Medium"), ("ClearSpeak_Fractions", "Auto")], expr, "89 durch 10")?; - test_prefs("de", "ClearSpeak", vec![("Verbosity", "Medium"), ("ClearSpeak_Fractions", "Ordinal")], expr, "89 zehnte")?; + test_prefs("de", "ClearSpeak", vec![("Verbosity", "Medium"), ("ClearSpeak_Fractions", "Ordinal")], expr, "89 Zehntel")?; return Ok(()); } @@ -172,14 +172,14 @@ fn nested_simple_fractions() -> Result<()> { "; - test_prefs("de", "ClearSpeak", vec![("ClearSpeak_Fractions", "Auto")], expr, "1 hälfte durch 2 dritte")?; - test_prefs("de", "ClearSpeak", vec![("ClearSpeak_Fractions", "Ordinal")], expr, "1 hälfte durch 2 dritte")?; + test_prefs("de", "ClearSpeak", vec![("ClearSpeak_Fractions", "Auto")], expr, "1 hälfte durch 2 Drittel")?; + test_prefs("de", "ClearSpeak", vec![("ClearSpeak_Fractions", "Ordinal")], expr, "1 hälfte durch 2 Drittel")?; test_prefs("de", "ClearSpeak", vec![("ClearSpeak_Fractions", "Over")], expr, "1 durch 2 durch 2 durch 3")?; test_prefs("de", "ClearSpeak", vec![("ClearSpeak_Fractions", "FracOver")], expr, "der bruch der bruch 1 durch 2 durch der bruch 2 durch 3")?; test_prefs("de", "ClearSpeak", vec![("ClearSpeak_Fractions", "General")], expr, "der bruch mit zähler der bruch mit zähler 1; und nenner 2; und nenner der bruch mit zähler 2; und nenner 3")?; - test_prefs("de", "ClearSpeak", vec![("ClearSpeak_Fractions", "EndFrac")], expr, "1 hälfte durch 2 dritte")?; + test_prefs("de", "ClearSpeak", vec![("ClearSpeak_Fractions", "EndFrac")], expr, "1 hälfte durch 2 Drittel")?; test_prefs("de", "ClearSpeak", vec![("ClearSpeak_Fractions", "GeneralEndFrac")], expr, "der bruch mit zähler der bruch mit zähler 1; und nenner 2; ende des bruchs; und nenner der bruch mit zähler 2; und nenner 3; ende des bruchs; ende des bruchs")?; test_prefs("de", "ClearSpeak", vec![("ClearSpeak_Fractions", "OverEndFrac")], expr, @@ -205,7 +205,7 @@ fn semi_nested_fraction() -> Result<()> { "; - test("de", "ClearSpeak", expr, "2 dritte x durch 6")?; + test("de", "ClearSpeak", expr, "2 Drittel x durch 6")?; return Ok(()); } From 5df8e8123eca59dd945f5779afb29e55518f2ee0 Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 03:18:00 +0200 Subject: [PATCH 111/120] German ClearSpeak: subset & superset --- Rules/Languages/de/unicode-full.yaml | 8 ++++---- Rules/Languages/de/unicode.yaml | 4 ++-- tests/Languages/de/ClearSpeak/sets.rs | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/Rules/Languages/de/unicode-full.yaml b/Rules/Languages/de/unicode-full.yaml index 8a98107f8..871e4e434 100644 --- a/Rules/Languages/de/unicode-full.yaml +++ b/Rules/Languages/de/unicode-full.yaml @@ -1102,13 +1102,13 @@ - "⊆": # 0x2286 - test: if: "$Verbosity!='Terse'" - then: [T: "ist ein"] # (en: 'is a', google translation) - - T: "Teilmenge oder gleich" # (en: 'subset of or equal to', MathPlayer: 'Teilmenge von odre gleich', google: 'teilmenge von oder gleich') + then: [T: "ist eine"] # (en: 'is a', google translation) + - T: "Teilmenge von oder gleich" # (en: 'subset of or equal to', MathPlayer: 'Teilmenge von odre gleich', google: 'teilmenge von oder gleich') - "⊇": # 0x2287 - test: if: "$Verbosity!='Terse'" - then: [T: "ist ein"] # (en: 'is a', google translation) - - T: "Obermenge oder gleich" # (en: 'superset of or equal to', MathPlayer: 'Obermenge von oder gleich', google: 'superset von oder gleich') + then: [T: "ist eine"] # (en: 'is a', google translation) + - T: "Obermenge von oder gleich" # (en: 'superset of or equal to', MathPlayer: 'Obermenge von oder gleich', google: 'superset von oder gleich') - "⊈": # 0x2288 - test: if: "$Verbosity!='Terse'" diff --git a/Rules/Languages/de/unicode.yaml b/Rules/Languages/de/unicode.yaml index bdd89fc76..bee3b6e7b 100644 --- a/Rules/Languages/de/unicode.yaml +++ b/Rules/Languages/de/unicode.yaml @@ -431,12 +431,12 @@ - test: if: "$Verbosity!='Terse'" then: [T: "ist eine"] # (en: 'is a', google translation) - - T: "teilmenge oder gleich" # (en: 'subset of or equal to', MathPlayer: 'Teilmenge von odre gleich', google: 'teilmenge von oder gleich') + - T: "teilmenge von oder gleich" # (en: 'subset of or equal to', MathPlayer: 'Teilmenge von odre gleich', google: 'teilmenge von oder gleich') - "⊇": # 0x2287 - test: if: "$Verbosity!='Terse'" then: [T: "ist eine"] # (en: 'is a', google translation) - - T: "obermenge oder gleich" # (en: 'superset of or equal to', MathPlayer: 'Obermenge von oder gleich', google: 'superset von oder gleich') + - T: "obermenge von oder gleich" # (en: 'superset of or equal to', MathPlayer: 'Obermenge von oder gleich', google: 'superset von oder gleich') # --- restored short definitions that differ from unicode-full --- - "–": [T: "halbgeviertstrich"] # 0x2013 (SRE: 'En Dash') diff --git a/tests/Languages/de/ClearSpeak/sets.rs b/tests/Languages/de/ClearSpeak/sets.rs index e8911f33f..e9f70e3cc 100644 --- a/tests/Languages/de/ClearSpeak/sets.rs +++ b/tests/Languages/de/ClearSpeak/sets.rs @@ -230,6 +230,24 @@ fn element_alone() -> Result<()> { } +#[test] +fn subset_and_superset_or_equal_include_von() -> Result<()> { + // Both inclusive set relations should form complete German relational phrases. + test( + "de", + "ClearSpeak", + "A⊆B", + "groß a, ist eine teilmenge von oder gleich, groß b", + )?; + test( + "de", + "ClearSpeak", + "A⊇B", + "groß a, ist eine obermenge von oder gleich, groß b", + )?; + Ok(()) +} + #[test] fn element_under_sum() -> Result<()> { let expr = " From 88404bfa21276daf86a4883f45d79483571ea225 Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 03:28:13 +0200 Subject: [PATCH 112/120] de: fix chemical state names --- Rules/Languages/de/SharedRules/general.yaml | 8 ++--- tests/Languages/de/chemistry.rs | 34 +++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/Rules/Languages/de/SharedRules/general.yaml b/Rules/Languages/de/SharedRules/general.yaml index 3098d76ac..9f4807946 100644 --- a/Rules/Languages/de/SharedRules/general.yaml +++ b/Rules/Languages/de/SharedRules/general.yaml @@ -1005,12 +1005,12 @@ - bookmark: "*[1]/@id" - test: - if: ".='s'" - then: [T: "solide"] # phrase(Boron is a 'solid' in its natural state) + then: [T: "fest"] # phrase(Boron is a 'solid' in its natural state) - else_if: ".='l'" - then: [T: "flüssigkeit"] # phrase(water is a 'liquid') + then: [T: "flüssig"] # phrase(water is a 'liquid') - else_if: ".='g'" - then: [T: "gas"] # phrase(hydrogen is a 'gas' ) - else: [T: "wässrige"] # phrase(an 'aqueous' solution is contained in water) + then: [T: "gasförmig"] # phrase(hydrogen is a 'gas' ) + else: [T: "wässrig"] # phrase(an 'aqueous' solution is contained in water) - pause: short - name: chemical-formula-operator-bond diff --git a/tests/Languages/de/chemistry.rs b/tests/Languages/de/chemistry.rs index 14bb1bfd1..63c46302d 100644 --- a/tests/Languages/de/chemistry.rs +++ b/tests/Languages/de/chemistry.rs @@ -40,6 +40,40 @@ fn sulfate() -> Result<()> { } +#[test] +fn chemical_state_names_are_german_adjectives() -> Result<()> { + // Chemical state suffixes should use the standard German adjectival names. + test_prefs( + "de", + "ClearSpeak", + vec![("Verbosity", "Terse")], + "Na(s)", + "groß n a, fest", + )?; + test_prefs( + "de", + "ClearSpeak", + vec![("Verbosity", "Terse")], + "H2O(l)", + "groß h, 2 groß o, flüssig", + )?; + test_prefs( + "de", + "ClearSpeak", + vec![("Verbosity", "Terse")], + "HCl(g)", + "groß h, groß c l, gasförmig", + )?; + test_prefs( + "de", + "ClearSpeak", + vec![("Verbosity", "Terse")], + "HCl(aq)", + "groß h, groß c l, wässrig", + )?; + Ok(()) +} + /* // ignored below #[test] From c17671cb3b7fca7609f54603015ed7fef036b155 Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 03:50:13 +0200 Subject: [PATCH 113/120] de: fix chemical reaction arrow --- Rules/Languages/de/SharedRules/general.yaml | 5 +--- tests/Languages/de/chemistry.rs | 26 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/Rules/Languages/de/SharedRules/general.yaml b/Rules/Languages/de/SharedRules/general.yaml index 9f4807946..b600b517f 100644 --- a/Rules/Languages/de/SharedRules/general.yaml +++ b/Rules/Languages/de/SharedRules/general.yaml @@ -1044,10 +1044,7 @@ - bookmark: "@id" - test: - if: ".='→' or .='⟶'" - then_test: - if: "$Verbosity='Terse'" - then: [T: "formen"] # phrase(hydrogen and oxygen 'forms' water ) - else: [T: "reagiert auf die form"] # phrase(hydrogen and oxygen 'reacts to form' water) + then: [T: "ergibt"] # phrase(hydrogen and oxygen 'forms/reacts to form' water) - else_if: ".='⇌' or .='🣑'" # U+01F8D1 then: [T: "ist im gleichgewicht mit"] # phrase(a reactant 'is in equilibrium with' a product) - else_if: ".='🣓'" # U+1F8D3 diff --git a/tests/Languages/de/chemistry.rs b/tests/Languages/de/chemistry.rs index 63c46302d..25b888ec2 100644 --- a/tests/Languages/de/chemistry.rs +++ b/tests/Languages/de/chemistry.rs @@ -74,6 +74,32 @@ fn chemical_state_names_are_german_adjectives() -> Result<()> { Ok(()) } +#[test] +fn reaction_arrow_uses_idiomatic_german() -> Result<()> { + // A reaction arrow should describe the resulting products idiomatically in German. + let expr = " + 2HCl+2Na + → + 2NaCl+ + H2 + "; + test_prefs( + "de", + "SimpleSpeak", + vec![("Verbosity", "Terse")], + expr, + "2, groß h, groß c l; plus 2 groß n a; ergibt; 2, groß n a, groß c l; plus groß h, 2", + )?; + test_prefs( + "de", + "SimpleSpeak", + vec![("Verbosity", "Verbose")], + expr, + "2, groß h, groß c l; plus 2 groß n a; ergibt; 2, groß n a, groß c l; plus groß h, index 2", + )?; + Ok(()) +} + /* // ignored below #[test] From 771dc68e4e836404e0c52b964969e6c078d0d7dc Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 04:09:31 +0200 Subject: [PATCH 114/120] de: fix integer exponentiation --- Rules/Languages/de/ClearSpeak_Rules.yaml | 8 +------- Rules/Languages/de/SimpleSpeak_Rules.yaml | 7 ++----- tests/Languages/de/shared.rs | 13 +++++++++++++ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Rules/Languages/de/ClearSpeak_Rules.yaml b/Rules/Languages/de/ClearSpeak_Rules.yaml index d2551ffef..01f8c9eba 100644 --- a/Rules/Languages/de/ClearSpeak_Rules.yaml +++ b/Rules/Languages/de/ClearSpeak_Rules.yaml @@ -326,13 +326,7 @@ replace: - x: "*[1]" - T: "hoch" - - test: - if: "*[2][.>0]" - then: [x: "ToOrdinal(*[2])"] - else: [x: "*[2]"] - - test: - if: "$ClearSpeak_Exponents != 'Ordinal'" - then: [T: "hoch"] + - x: "*[2]" - name: simple-negative-integer tag: power diff --git a/Rules/Languages/de/SimpleSpeak_Rules.yaml b/Rules/Languages/de/SimpleSpeak_Rules.yaml index e04749887..d23a47d7c 100644 --- a/Rules/Languages/de/SimpleSpeak_Rules.yaml +++ b/Rules/Languages/de/SimpleSpeak_Rules.yaml @@ -208,11 +208,8 @@ match: "*[2][self::m:mn][not(contains(., '.'))]" replace: - x: "*[1]" - - T: "zu dem" # phrase(15 raised 'to the' second power equals 225) - - test: - if: "*[2][.>0]" - then: [x: "ToOrdinal(*[2])"] - else: [x: "*[2]"] + - T: "hoch" # phrase(15 raised 'to the' fourth power equals 50625) + - x: "*[2]" - name: simple-negative-integer tag: power match: diff --git a/tests/Languages/de/shared.rs b/tests/Languages/de/shared.rs index 34518d9e0..53d1468db 100644 --- a/tests/Languages/de/shared.rs +++ b/tests/Languages/de/shared.rs @@ -3,6 +3,19 @@ use crate::common::*; use anyhow::Result; +#[test] +fn ordinary_integer_powers() -> Result<()> { + // Ordinary integer exponents use the idiomatic German "hoch" construction. + let fourth_power = "x4"; + test("de", "ClearSpeak", fourth_power, "x hoch 4")?; + test("de", "SimpleSpeak", fourth_power, "x hoch 4")?; + + let zeroth_power = "x0"; + test("de", "ClearSpeak", zeroth_power, "x hoch 0")?; + test("de", "SimpleSpeak", zeroth_power, "x hoch 0")?; + Ok(()) +} + #[test] fn modified_vars() -> Result<()> { let expr = " From c6a33fad46c8a3cc83c91c771160113c381023ee Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 04:27:02 +0200 Subject: [PATCH 115/120] de: fix exponentiation by arbitrary exponents --- Rules/Languages/de/ClearSpeak_Rules.yaml | 4 ---- Rules/Languages/de/SimpleSpeak_Rules.yaml | 1 - tests/Languages/de/shared.rs | 9 +++++++++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Rules/Languages/de/ClearSpeak_Rules.yaml b/Rules/Languages/de/ClearSpeak_Rules.yaml index 01f8c9eba..c1dd81eb5 100644 --- a/Rules/Languages/de/ClearSpeak_Rules.yaml +++ b/Rules/Languages/de/ClearSpeak_Rules.yaml @@ -349,10 +349,6 @@ - x: "*[1]" - T: "hoch" - x: "*[2]" - - pronounce: [text: "-th", ipa: "θ", sapi5: "th", eloquence: "T"] - - test: - if: "$ClearSpeak_Exponents != 'Ordinal'" - then: [T: "hoch"] # 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 diff --git a/Rules/Languages/de/SimpleSpeak_Rules.yaml b/Rules/Languages/de/SimpleSpeak_Rules.yaml index d23a47d7c..4d48fd27e 100644 --- a/Rules/Languages/de/SimpleSpeak_Rules.yaml +++ b/Rules/Languages/de/SimpleSpeak_Rules.yaml @@ -226,7 +226,6 @@ - x: "*[1]" - T: "hoch" # phrase(15 raised 'to the' second power equals 225) - x: "*[2]" - - pronounce: [text: "-th", ipa: "θ", sapi5: "th", eloquence: "T"] - name: simple tag: power diff --git a/tests/Languages/de/shared.rs b/tests/Languages/de/shared.rs index 53d1468db..d69acf25a 100644 --- a/tests/Languages/de/shared.rs +++ b/tests/Languages/de/shared.rs @@ -16,6 +16,15 @@ fn ordinary_integer_powers() -> Result<()> { Ok(()) } +#[test] +fn variable_powers() -> Result<()> { + // Variable exponents should not acquire an English ordinal suffix or a second "hoch". + let expr = "2x"; + test("de", "ClearSpeak", expr, "2 hoch x")?; + test("de", "SimpleSpeak", expr, "2 hoch x")?; + Ok(()) +} + #[test] fn modified_vars() -> Result<()> { let expr = " From 6f24b27a37cb986e1434c5dc76aeb65adc45f66d Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 04:37:54 +0200 Subject: [PATCH 116/120] de: add vector norm --- Rules/Languages/de/definitions.yaml | 1 + tests/Languages/de/intent.rs | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/Rules/Languages/de/definitions.yaml b/Rules/Languages/de/definitions.yaml index de78c7530..163c3f86d 100644 --- a/Rules/Languages/de/definitions.yaml +++ b/Rules/Languages/de/definitions.yaml @@ -29,6 +29,7 @@ "plus": "infix=plus || prefix=positiv", "real-part": "function=der reale teil", "transpose": "postfix=transponieren || function=transponieren", + "magnitude": "function=; Norm: Norm: Norm; Ende Norm", "norm": "function=; norm: norm: norm; ende norm", "trace": "function=; spur : spur: die spur; ende spur", "dimension": "function=; dimension : dimension: die dimension; ende dimension", diff --git a/tests/Languages/de/intent.rs b/tests/Languages/de/intent.rs index 62e711db6..d31af9d9b 100644 --- a/tests/Languages/de/intent.rs +++ b/tests/Languages/de/intent.rs @@ -3,6 +3,15 @@ use crate::common::*; use anyhow::Result; +#[test] +fn magnitude_is_localized_as_norm() -> Result<()> { + // Double bars infer the magnitude intent, which should use the German term "Norm". + let expr = "‖x‖"; + test("de", "ClearSpeak", expr, "Norm von x")?; + test("de", "SimpleSpeak", expr, "Norm von x")?; + Ok(()) +} + #[test] fn clear_speak_short_pause_intent() -> Result<()> { // A short pause intent should survive intent inference and render as a comma. From 11736b5bb3638ae30ab955a1a8cdfee8e196333d Mon Sep 17 00:00:00 2001 From: mgros Date: Tue, 22 Sep 2026 04:42:03 +0200 Subject: [PATCH 117/120] de: fix "transpose" --- Rules/Languages/de/definitions.yaml | 2 +- tests/Languages/de/shared.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Rules/Languages/de/definitions.yaml b/Rules/Languages/de/definitions.yaml index 163c3f86d..427eec726 100644 --- a/Rules/Languages/de/definitions.yaml +++ b/Rules/Languages/de/definitions.yaml @@ -28,7 +28,7 @@ "minus": "infix=minus || prefix=negativ", "plus": "infix=plus || prefix=positiv", "real-part": "function=der reale teil", - "transpose": "postfix=transponieren || function=transponieren", + "transpose": "postfix=transponiert || function=die Transponierte", "magnitude": "function=; Norm: Norm: Norm; Ende Norm", "norm": "function=; norm: norm: norm; ende norm", "trace": "function=; spur : spur: die spur; ende spur", diff --git a/tests/Languages/de/shared.rs b/tests/Languages/de/shared.rs index d69acf25a..93d570108 100644 --- a/tests/Languages/de/shared.rs +++ b/tests/Languages/de/shared.rs @@ -3,6 +3,18 @@ use crate::common::*; use anyhow::Result; +#[test] +fn transpose_uses_grammatical_german_forms() -> Result<()> { + // A superscript T uses the participle, while a function intent names the transpose. + let postfix = "AT"; + test("de", "ClearSpeak", postfix, "groß a transponiert")?; + test("de", "SimpleSpeak", postfix, "groß a transponiert")?; + + let function = "A"; + test("de", "ClearSpeak", function, "die Transponierte von groß a")?; + Ok(()) +} + #[test] fn ordinary_integer_powers() -> Result<()> { // Ordinary integer exponents use the idiomatic German "hoch" construction. From bb775c5a7b8d5a9d50b65629106023bdefbed1df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Gro=C3=9F?= Date: Tue, 22 Sep 2026 08:28:44 +0200 Subject: [PATCH 118/120] replace lamda with lambda (#789) * replace lamda with lambda * fix line endings --- Rules/Languages/de/unicode.yaml | 2 +- Rules/Languages/id/unicode-full.yaml | 4 ++-- Rules/Languages/id/unicode.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Rules/Languages/de/unicode.yaml b/Rules/Languages/de/unicode.yaml index bee3b6e7b..f8b0ec641 100644 --- a/Rules/Languages/de/unicode.yaml +++ b/Rules/Languages/de/unicode.yaml @@ -214,7 +214,7 @@ - "θ": [T: "theta"] # 0x3b8 - "ι": [T: "iota"] # 0x3b9 (google: 'jota') - "κ": [T: "kappa"] # 0x3ba - - "λ": [T: "lamda"] # 0x3bb (en: 'lambda', google: 'lambda') + - "λ": [T: "lambda"] # 0x3bb (en: 'lambda', google: 'lambda') - "μ": [T: "mü"] # 0x3bc - "ν": [T: "nü"] # 0x3bd - "ξ": [T: "xi"] # 0x3be (en: 'zai', google: 'zai') diff --git a/Rules/Languages/id/unicode-full.yaml b/Rules/Languages/id/unicode-full.yaml index 5591060fd..65af01886 100644 --- a/Rules/Languages/id/unicode-full.yaml +++ b/Rules/Languages/id/unicode-full.yaml @@ -314,7 +314,7 @@ - test: if: "($Blind or $Verbosity!='Terse')" then: [T: huruf besar] - - T: lamda + - T: lambda - "Μ": # 0x39c - test: if: "($Blind or $Verbosity!='Terse')" @@ -400,7 +400,7 @@ - "θ": [T: theta] # 0x3b8 - "ι": [T: iota] # 0x3b9 - "κ": [T: kappa] # 0x3ba - - "λ": [T: lamda] # 0x3bb + - "λ": [T: lambda] # 0x3bb - "μ": [T: mu] # 0x3bc - "ν": [T: nu] # 0x3bd - "ξ": [T: zai] # 0x3be diff --git a/Rules/Languages/id/unicode.yaml b/Rules/Languages/id/unicode.yaml index 11f586349..ebc0a1909 100644 --- a/Rules/Languages/id/unicode.yaml +++ b/Rules/Languages/id/unicode.yaml @@ -243,7 +243,7 @@ - "θ": [T: theta] # 0x3b8 - "ι": [T: iota] # 0x3b9 - "κ": [T: kappa] # 0x3ba -- "λ": [T: lamda] # 0x3bb +- "λ": [T: lambda] # 0x3bb - "μ": [T: mu] # 0x3bc - "ν": [T: nu] # 0x3bd - "ξ": [T: zai] # 0x3be From 24eaee398ca4b5c6bc308efcfa66c73dbfd888d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dziwisz?= Date: Tue, 22 Sep 2026 08:31:10 +0200 Subject: [PATCH 119/120] =?UTF-8?q?fix(pl):=20przywr=C3=B3=C4=87=20szablon?= =?UTF-8?q?y=20sum=20i=20iloczyn=C3=B3w=20po=20naprawie=20s=C5=82owa=20?= =?UTF-8?q?=C5=82=C4=85cz=C4=85cego=20(#783)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Rules/Languages/pl/definitions.yaml | 19 +++++++++++-------- tests/Languages/pl/definitions.rs | 8 ++++---- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Rules/Languages/pl/definitions.yaml b/Rules/Languages/pl/definitions.yaml index 0f212c4e4..e3a8bc1db 100644 --- a/Rules/Languages/pl/definitions.yaml +++ b/Rules/Languages/pl/definitions.yaml @@ -60,14 +60,12 @@ ### Sequence and Series - # UWAGA: NIE używamy tu szablonów arności ("| po | od,do"), choć angielski je ma. - # Powód (zmierzone 31.08.2026): intent_function_glue_before w src/infer_intent.rs - # zwraca dla ostatniego argumentu ZASZYTE W KODZIE angielskie "of", więc polskie - # wyjście brzmiałoby "suma po i of x". To defekt rdzenia dotyczący każdego języka - # poza angielskim; węgierski uniknął go tak samo, zostawiając prostą postać. - # Separator binarny (jedna opcja "| słowo") działa poprawnie i jest używany niżej. - "sum":"function=suma", - "product":"function=iloczyn", + # Szablony arności działają od czasu, gdy słowo łączące przestało być zaszyte + # w kodzie (issue #739): ostatni argument poprzedza teraz FunctionApplicationWord + # z tego pliku, czyli polskie "z". Słowa "przez" i "od,do" dobrane tak, jak mówią + # o tym istniejące reguły dużych operatorów ("suma przez wielka s z i"). + "sum":"function=suma | przez | od,do", + "product":"function=iloczyn | przez | od,do", ### Elementary classical functions @@ -298,6 +296,11 @@ "chemistry-concentration": "function=; stężenie: stężenie: stężenie; koniec stężenia", } + # The connector word to say for functional 'IntentMappings' (in English, "of") + # This is likely the word used for U+2061 in unicode.yaml, but for technical reasons needs to be defined here + # vector of length=1 because scalars not supported +- FunctionApplicationWord: ["z"] + # Names of functions that in terse mode don't say "of" (or it's equivalent in other languages) - TerseFunctionNames: { "divergence", "gradient", "curl" diff --git a/tests/Languages/pl/definitions.rs b/tests/Languages/pl/definitions.rs index 338f7ff95..d009d9120 100644 --- a/tests/Languages/pl/definitions.rs +++ b/tests/Languages/pl/definitions.rs @@ -422,7 +422,7 @@ fn sequence_and_series_intents() -> Result<()> { x "#, - "suma z i przecinek, x", + "suma przez i z x", ), ( "sum-3", @@ -434,7 +434,7 @@ fn sequence_and_series_intents() -> Result<()> { x "#, - "suma z i przecinek, n przecinek, x", + "suma od i do n z x", ), ( "product-1", @@ -455,7 +455,7 @@ fn sequence_and_series_intents() -> Result<()> { x "#, - "iloczyn z i przecinek, x", + "iloczyn przez i z x", ), ( "product-3", @@ -467,7 +467,7 @@ fn sequence_and_series_intents() -> Result<()> { x "#, - "iloczyn z i przecinek, n przecinek, x", + "iloczyn od i do n z x", ), ]; From e46e7dbcc89f11d304de660ca1444fdf8322acb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Gro=C3=9F?= Date: Tue, 22 Sep 2026 08:42:49 +0200 Subject: [PATCH 120/120] Refactor/simplify prefs rs (#780) * remove pointless cloning * simplify logic flow of is_user_pref * simplify path joins * Add comment to clarify update condition Added a comment to prevent unnecessary updates if the value hasn't changed. --------- Co-authored-by: NSoiffer --- src/prefs.rs | 68 ++++++++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/prefs.rs b/src/prefs.rs index 89748119f..f515ca483 100644 --- a/src/prefs.rs +++ b/src/prefs.rs @@ -133,12 +133,11 @@ impl Preferences{ verify_keys(doc, "Other", file_name)?; } - let prefs = &mut base_prefs.prefs; - add_prefs(prefs, &doc["Speech"], "", file_name); - add_prefs(prefs, &doc["Navigation"], "", file_name); - add_prefs(prefs, &doc["Braille"], "", file_name); - add_prefs(prefs, &doc["Other"], "", file_name); - return Ok( Preferences{ prefs: prefs.to_owned() } ); + add_prefs(&mut base_prefs.prefs, &doc["Speech"], "", file_name); + add_prefs(&mut base_prefs.prefs, &doc["Navigation"], "", file_name); + add_prefs(&mut base_prefs.prefs, &doc["Braille"], "", file_name); + add_prefs(&mut base_prefs.prefs, &doc["Other"], "", file_name); + return Ok(base_prefs); @@ -317,8 +316,7 @@ impl PreferenceManager { let mut prefs = Preferences::default(); - let mut system_prefs_file = self.rules_dir.to_path_buf(); - system_prefs_file.push("prefs.yaml"); + let system_prefs_file: PathBuf = self.rules_dir.join("prefs.yaml"); if is_file_shim(&system_prefs_file) { let defaults = DEFAULT_USER_PREFERENCES.with(|defaults| defaults.clone()); prefs = Preferences::read_prefs_file(&system_prefs_file, defaults)?; @@ -363,11 +361,11 @@ impl PreferenceManager { let language = self.pref_to_string("Language"); let language = if language.as_str() == "Auto" {"en"} else {language.as_str()}; // avoid 'temp value dropped while borrowed' error - let language_dir = rules_dir.to_path_buf().join("Languages"); + let language_dir = rules_dir.join("Languages"); self.set_speech_files(&language_dir, language, None)?; // also sets style file let braille_code = self.pref_to_string("BrailleCode"); - let braille_dir = rules_dir.to_path_buf().join("Braille"); + let braille_dir = rules_dir.join("Braille"); self.set_braille_files(&braille_dir, &braille_code)?; return Ok(()); } @@ -426,12 +424,12 @@ impl PreferenceManager { let new_language = new_prefs.prefs.get("Language").unwrap(); debug!("set_files_based_on_changes: old_language={old_language:?}, new_language={new_language:?}"); if old_language != new_language { - let language_dir = self.rules_dir.to_path_buf().join("Languages"); + let language_dir = self.rules_dir.join("Languages"); self.set_speech_files(&language_dir, new_language.as_str().unwrap(), None)?; // also sets style file } else { let old_speech_style = self.user_prefs.prefs.get("SpeechStyle").unwrap(); let new_speech_style = new_prefs.prefs.get("SpeechStyle").unwrap(); - let language_dir = self.rules_dir.to_path_buf().join("Languages"); + let language_dir = self.rules_dir.join("Languages"); if old_speech_style != new_speech_style { self.set_speech_files(&language_dir, new_language.as_str().unwrap(), new_speech_style.as_str())?; } @@ -440,7 +438,7 @@ impl PreferenceManager { let old_braille_code = self.user_prefs.prefs.get("BrailleCode").unwrap(); let new_braille_code = new_prefs.prefs.get("BrailleCode").unwrap(); if old_braille_code != new_braille_code { - let braille_code_dir = self.rules_dir.to_path_buf().join("Braille"); + let braille_code_dir = self.rules_dir.join("Braille"); self.set_braille_files(&braille_code_dir, new_braille_code.as_str().unwrap())?; // also sets style file } @@ -566,7 +564,7 @@ impl PreferenceManager { let mut alternative_style_file = None; // back up in case we don't find the target style in lang_dir let looking_for_style_file = file_name.ends_with("_Rules.yaml"); for os_path in lang_dir.ancestors() { // ancestor returns self and ancestors - let path = PathBuf::from(os_path).join(file_name); + let path = os_path.join(file_name); // debug!("find_file: checking file: {}", path.to_string_lossy()); if is_file_shim(&path) { // we make an exception for definitions.yaml -- there a language specific checks for Hundreds, etc @@ -634,8 +632,7 @@ impl PreferenceManager { fn get_language_dir(rules_dir: &Path, lang: &str, default_lang: Option<&str>) -> Result { // return 'Rules/Language/fr', 'Rules/Language/en/gb', etc, if they exist. // fall back to main language, and then to default_dir if language dir doesn't exist - let mut full_path = rules_dir.to_path_buf(); - full_path.push(lang.replace('-', std::path::MAIN_SEPARATOR_STR)); + let full_path = rules_dir.join(lang.replace('-', std::path::MAIN_SEPARATOR_STR)); for parent in full_path.ancestors() { if parent == rules_dir { break; @@ -710,21 +707,17 @@ impl PreferenceManager { !value.chars().all(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '_' | '-')) { bail!("{} is an invalid value! Must contains only ascii letters, '_', or'-'", key); } - + // don't do an update if the value hasn't changed - let mut is_user_pref = true; - if let Some(pref_value) = self.api_prefs.prefs.get(key) { - if pref_value.as_str().unwrap() != value { - is_user_pref = false; - self.reset_files_from_preference_change(key, value)?; - } - } else if let Some(pref_value) = self.user_prefs.prefs.get(key) { - if pref_value.as_str().unwrap() != value { - self.reset_files_from_preference_change(key, value)?; - } - } else { - bail!("{} is an unknown MathCAT preference!", key); + let api_pref: Option<&Yaml> = self.api_prefs.prefs.get(key); + let Some(pref_value) = api_pref.or_else(|| self.user_prefs.prefs.get(key)) else { + bail!("{key} is an unknown MathCAT preference!"); + }; + if pref_value.as_str().unwrap() == value { + return Ok( () ); } + let is_user_pref: bool = api_pref.is_none(); + self.reset_files_from_preference_change(key, value)?; // debug!("Setting ({}) {} to '{}'", if is_user_pref {"user"} else {"sys"}, key, value); if is_user_pref { @@ -755,7 +748,7 @@ impl PreferenceManager { } let changed_pref = if changed_pref == "LanguageAuto" {"Language"} else {changed_pref}; - let language_dir = self.rules_dir.to_path_buf().join("Languages"); + let language_dir = self.rules_dir.join("Languages"); match changed_pref { "Language" => { self.set_speech_files(&language_dir, changed_value, None)?; @@ -768,7 +761,7 @@ impl PreferenceManager { crate::speech::invalidate_speech_style_caches(); }, "BrailleCode" => { - let braille_dir = self.rules_dir.to_path_buf().join("Braille"); + let braille_dir = self.rules_dir.join("Braille"); self.set_braille_files(&braille_dir, changed_value)?; crate::speech::invalidate_braille_caches(); }, @@ -1170,6 +1163,19 @@ cfg_if::cfg_if! {if #[cfg(not(feature = "include-zip"))] { }); } + /// Setting `TTS` to its existing value should not copy it from API preferences into user preferences. + #[test] + fn unchanged_api_string_pref_is_noop() { + let mut pref_manager = PreferenceManager { + api_prefs: Preferences::api_defaults(), + ..PreferenceManager::default() + }; + + pref_manager.set_string_pref("TTS", "none").unwrap(); + + assert!(!pref_manager.user_prefs.prefs.contains_key("TTS")); + } + /// #262: MathCAT must notice when a rule file on disk changes and reload it. /// This copies the rule files it needs (`en`, `zz`, and `Nemeth`) into a uniquely-named /// temp dir, so the test can freely modify files and run in parallel without touching the