﻿
Sub TibetanSpellchecker()

    ' ======================================================================
    '
    '                      Tibetan Spell-checker
    '
    '                     ver. 1.0  (2011-Dec-16)
    '
    '                       by Paul G. Hackett
    '
    '                           in VBScript
    '
    ' ======================================================================
    ' ======================================================================
    ' Documentation
    ' ======================================================================
    ' ======================================================================
    '
    ' About:
    '
    ' Tibetan Spell-checker was based on original Tibetan sorting algorithms written
    ' by Paul G. Hackett in 1992.  In 2011, the code was repurposed as a Tibetan
    ' Spell-checker and re-written in Microsoft VBScript (Visual Basic).  
    '
    ' Tibetan spell-checking can be performed in a variety of ways: inclusive 
    ' rule-based, exclusive rule-based, and dictionary look-up.  This VBScript 
    ' was based on a Tibetan sort algorithm written in 1992, that performed 
    ' exclusive recognition of spelling errors as a consequence of its design. 
    ' Given the cumbersome nature of Visual Basic as a programming language, 
    ' an exclusive spell-checker (and dictionary look-up) was deemed inefficient 
    ' in terms of computational overhead, hence although the original identification 
    ' criteria were retained, the algorithms were re-purposed for an inclusive 
    ' spell-checker.  This is the v. 1.0 release.  This first implementation 
    ' is neither as efficient nor necessarily as robust as it could be, or 
    ' even very pretty, but should serve most purposes. Streamlining will 
    ' require a subsequent version.
    '
    ' Three levels of spell-checking (with different color-coding) are performed
    ' by this script: red (warning) refers to normalization errors (mis-sequenced
    ' or mistaken letter usage  -- including "full-height-ra" U+0F6A, and 
    ' "sub-joined consonant 'a" U+0FB0 instead of U+0F71), yellow refers to 
    ' non-normative Tibetan letter combinations (possible typographic errors), 
    ' while blue refers to possible Sanskrit and other foreign language 
    ' transliterations, or scribal abbreviations.
    '
    ' This version of the Tibetan Spell-checker is being made available for public
    ' use by the author under the GNU General Public License (GNU GPL).
    '
    '                ------------------------------------------
    '
    ' Tibetan Spell-checker. A VBScript to perform spell-checking on Unicode Tibetan text.
    ' Copyright (C) 2011  Paul G. Hackett
    '
    ' This program is free software: you can redistribute it and/or modify
    ' it under the terms of the GNU General Public License as published by
    ' the Free Software Foundation, either version 3 of the License, or
    ' (at your option) any later version.
    '
    ' This program is distributed in the hope that it will be useful,
    ' but WITHOUT ANY WARRANTY; without even the implied warranty of
    ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    ' GNU General Public License for more details.
    '
    ' You should have received a copy of the GNU General Public License
    ' along with this program.  If not, see <http://www.gnu.org/licenses/>.
    '
    '                ------------------------------------------
    '
    ' Please address any feedback, bug reports, or modification requests to:
    '     "Paul Hackett" <ph2046@columbia.edu>
    '
    ' ======================================================================
    '
    ' Installation and Use:
    '
    ' 1) Because of limitations in the most current version of Microsoft Office
    ' for the Mac (Office 2011), this VisualBasic script *ONLY* works on the Windows
    ' version of Microsoft Office.  Although it may be possible to port the
    ' necessary RegEx library to the Mac environment, I have not attempted this.
    '
    ' 2) The TibetanSpellchecker is installed and accessed via the "Developer" menu
    ' in Microsoft Word.  To enable the "Developer" Menu under MSOffice 2011,
    ' navigate to "File" --> "Options" --> "Customize Ribbon", and activate the
    ' "Developer" menu by placing a "checkmark" in its box. Then click "OK".
    '
    ' 3) To install and use the TibetanSpellchecker the Regular Expression object
    ' library must be activated.  From the "Developer" tab, launch "Visual Basic";
    ' under the "Tools" menu select "References...", locate the object library
    ' "Microsoft VBScript Regular Expressions 5.5" and enable it by placing
    ' a checkmark in the box next to its name. Click "OK" to close the dialog box.
    ' Then under the "File" menu, select "Save Normal".
    '
    ' 3) To install the TibetanSpellchecker, from the "Developer" tab, launch 
    ' "Visual Basic".  In the left pane, locate "Normal" and expand its menu.
    ' Under that menu, expand the menu for "Miscrosoft Word Objects".  Double-click
    ' (to open) the item entitled "ThisDocument".  Copy and paste the entirety
    ' of this subroutine ("Sub TibetanSpellchecker()") into "ThisDocument".
    ' Under the "File" menu, select "Close and Return to Microsoft Word".
    '
    ' 4) The TibetanSpellchecker can be invoked in one of two ways: (a) via the
    ' "Developer" or "View" tabs, by clicking on the "Macros" icon and clicking on
    ' "TibetanSpellchecker", or (b) by assigning a keyboard shortcut to the macro.
    ' We strongly suggest that before running the Tibetan Spell-checker, the 
    ' document "View" option should be changed to "Draft" since otherwise Microsoft
    ' Word will attempt to perform repagination after every search  and drastically
    ' slow down the spell-checking process.
    '
    ' ======================================================================
    '
    ' Revision History:
    '
    '     ver. 1.0beta (2011-Dec-14): Initial beta release
    '     ver. 1.0 (2011-Dec-16): Formal ver 1.0 release
    '
    ' ======================================================================
    ' ======================================================================

	' first set of letter combos

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "([\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{8,}|[\u0F40-\u0F68]{5,}|[\u0F40-\u0F68]{3}[\u0F40-\u0F65\u0F67\u0F68]|[\u0F40\u0F41\u0F43-\u0F50\u0F52-\u0F55\u0F57\u0F59-\u0F5F\u0F61-\u0F68][\u0F40-\u0F5F\u0F61-\u0F68][\u0F40-\u0F65\u0F67\u0F68]|[\u0F40\u0F41\u0F43-\u0F50\u0F52-\u0F55\u0F57\u0F59-\u0F5F\u0F61-\u0F68][\u0F40\u0F41\u0F45-\u0F50\u0F52\u0F54\u0F55\u0F57\u0F59-\u0F68][\u0F66])"
    tibetanLetterRegExp.Global = True

    Dim tibetanWrongWords As MatchCollection
    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' more letter combos (1)

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "([\u0F42\u0F51\u0F56\u0F58\u0F60][\u0F40\u0F41\u0F42\u0F44-\u0F49\u0F4F-\u0F51\u0F53-\u0F56\u0F58-\u0F5B\u0F5D-\u0F64\u0F66-\u0F68][\u0F40\u0F41\u0F43\u0F45-\u0F50\u0F52\u0F54\u0F55\u0F57\u0F59-\u0F5F\u0F61\u0F64\u0F65\u0F67\u0F68][\u0F72\u0F74\u0F7A\u0F7C]?)"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' more letter combos (2)

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "([\u0F40\u0F41\u0F43\u0F45-\u0F50\u0F52\u0F54\u0F55\u0F57\u0F59-\u0F5F\u0F61\u0F64\u0F65\u0F67\u0F68][\u0FB1\u0FB2\u0FB3\u0FAD\u0F72\u0F74\u0F7A\u0F7C]?[\u0F42\u0F44\u0F51\u0F53\u0F56\u0F58\u0F60\u0F62\u0F63\u0F66][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{2,}|[\u0F40\u0F41\u0F43\u0F45-\u0F50\u0F52\u0F54\u0F55\u0F57\u0F59-\u0F5F\u0F61\u0F64\u0F65\u0F67\u0F68][\u0F60][\u0F72\u0F74\u0F7A\u0F7C\u0F44][\u0F42\u0F44\u0F51\u0F53\u0F56\u0F58\u0F60\u0F62\u0F63\u0F66][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{1,})"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' more letter combos (3)

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "([\u0F42\u0F51\u0F56\u0F58\u0F60]?[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC][\u0FB1\u0FB2\u0FB3\u0FAD\u0F72\u0F74\u0F7A\u0F7C]{1,}[\u0F42\u0F44\u0F51\u0F53\u0F56\u0F58\u0F62\u0F63\u0F66][\u0F40-\u0F65\u0F67-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{1,}[\u0F40-\u0F6C\u0F90-\u0FBC]{0,})"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' more letter combos (4)

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F40\u0F41\u0F43\u0F45-\u0F50\u0F52\u0F54\u0F55\u0F57\u0F59-\u0F5F\u0F61\u0F64\u0F65\u0F67\u0F68][\u0F72\u0F74\u0F7A\u0F7C][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F40\u0F41\u0F43\u0F45-\u0F50\u0F52\u0F54\u0F55\u0F57\u0F59-\u0F5F\u0F61\u0F64\u0F65\u0F67\u0F68][\u0F72\u0F74\u0F7A\u0F7C][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' more letter combos (5)

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F40\u0F41\u0F42\u0F44-\u0F49\u0F4F-\u0F51\u0F53-\u0F56\u0F58-\u0F5B\u0F5D-\u0F64\u0F66-\u0F68][\u0F90\u0F91\u0F92\u0F94-\u0F99\u0F9F-\u0FA1\u0FA3-\u0FA6\u0FA8-\u0FAB\u0FAD-\u0FB4\u0FB6-\u0FB8]{1,}[\u0F72\u0F74\u0F7A\u0F7C]?[\u0F40\u0F41\u0F42\u0F44-\u0F49\u0F4F-\u0F51\u0F53-\u0F56\u0F58-\u0F5B\u0F5D-\u0F64\u0F66-\u0F68][\u0F90\u0F91\u0F92\u0F94-\u0F99\u0F9F-\u0FA1\u0FA3-\u0FA6\u0FA8-\u0FAB\u0FAD-\u0FB4\u0FB6-\u0FB8][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' more letter combos (6)

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}([\u0FB1\u0FAD]{2}|[\u0F42\u0F51\u0F58\u0F60][\u0F62\u0F63\u0F66][\u0F90-\u0FBC])[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' bad prefixes

    Set tibetanLetterRegExp = New RegExp

    tibetanLetterRegExp.Pattern = "[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}([\u0F42][\u0F40\u0F41\u0F46\u0F47\u0F50\u0F5A\u0F54\u0F55\u0F5B\u0F5D\u0F67]|[\u0F51][\u0F41\u0F45-\u0F50\u0F55\u0F59-\u0F5F\u0F61\u0F64\u0F67]|[\u0F56][\u0F41\u0F46\u0F47\u0F49\u0F50\u0F54\u0F55\u0F5A\u0F5B\u0F5D\u0F61\u0F67]|[\u0F58][\u0F40\u0F45\u0F4F\u0F54\u0F55\u0F59\u0F5D-\u0F5F\u0F61\u0F64\u0F67]|[\u0F60][\u0F40\u0F45\u0F49\u0F54\u0F4F\u0F59\u0F5E\u0F5F\u0F61\u0F64\u0F5D\u0F67])[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' bad prefixes (2)

    Set tibetanLetterRegExp = New RegExp

    tibetanLetterRegExp.Pattern = "[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}([\u0F42][\u0F42\u0F44\u0F56\u0F58\u0F62\u0F63][\u0FB1\u0FB2\u0FB3\u0FAD\u0F72\u0F74\u0F7A\u0F7C\u0F42\u0F44\u0F51\u0F53\u0F56\u0F58\u0F60\u0F62\u0F63]|[\u0F51][\u0F53\u0F62\u0F63\u0F66][\u0FB1\u0FB2\u0FB3\u0FAD\u0F72\u0F74\u0F7A\u0F7C\u0F42\u0F44\u0F51\u0F53\u0F56\u0F58\u0F60\u0F62\u0F63]|[\u0F56][\u0F44\u0F53\u0F56\u0F58\u0F63][\u0FB1\u0FB2\u0FB3\u0FAD\u0F72\u0F74\u0F7A\u0F7C\u0F42\u0F44\u0F51\u0F53\u0F56\u0F58\u0F60\u0F62\u0F63]|[\u0F58][\u0F56\u0F58\u0F62\u0F63\u0F66][\u0FB1\u0FB2\u0FB3\u0FAD\u0F72\u0F74\u0F7A\u0F7C\u0F42\u0F44\u0F51\u0F53\u0F56\u0F58\u0F60\u0F62\u0F63]|[\u0F60][\u0F44\u0F53\u0F58\u0F63\u0F66][\u0FB1\u0FB2\u0FB3\u0FAD\u0F72\u0F74\u0F7A\u0F7C\u0F42\u0F44\u0F51\u0F53\u0F56\u0F58\u0F60\u0F62\u0F63])[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' bad ra-mgo combos

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F62][\u0F91\u0F93\u0F95\u0F96\u0F9A-\u0F9E\u0FA0\u0FA2\u0FA4\u0FA5\u0FA7\u0FAA\u0FAC\u0FAE-\u0FB2\u0FB4-\u0FBC][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' bad ra-mgo combos (2)

    Set tibetanLetterRegExp = New RegExp

    tibetanLetterRegExp.Pattern = "([\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}([\u0F62][\u0F90\u0F92\u0FA8][\u0FB2\u0FAD]|[\u0F62][\u0F94\u0F97\u0F99\u0F9F\u0FA1\u0FA3\u0FA6\u0FAB\u0FB3][\u0FB2\u0FB1\u0FAD]|[\u0F62][\u0FA9][\u0FB2\u0FB1]|[\u0F56][\u0F62][\u0FA8][\u0FB2\u0FB1])[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,})"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' bad la-mgo combos

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F63][\u0F91\u0F93\u0F96\u0F99-\u0F9E\u0FA0\u0FA2\u0FA3\u0FA5\u0FA7-\u0FB1\u0FB4-\u0FB6\u0FB8-\u0FBC][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' bad la-mgo combos (2)

    Set tibetanLetterRegExp = New RegExp

    tibetanLetterRegExp.Pattern = "([\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}([\u0F63][\u0F90\u0F92\u0F94\u0F95\u0F97\u0F9F\u0FA1\u0FA4\u0FA6\u0FB7][\u0FB2\u0FB1\u0FAD]|[\u0F56][\u0F63][\u0F90\u0F92\u0F94\u0F95\u0F97\u0FA4\u0FA6\u0FB7])[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,})"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' bad sa-mgo combos

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F66][\u0F91\u0F93\u0F95\u0F96\u0F97\u0F9A-\u0F9E\u0FA0\u0FA2\u0FA5\u0FA7\u0FAA-\u0FB1\u0FB4-\u0FBC][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' bad sa-mgo combos (2)

    Set tibetanLetterRegExp = New RegExp

    tibetanLetterRegExp.Pattern = "([\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}([\u0F66][\u0F94\u0F99\u0F9F\u0FA1\u0FA9][\u0FB2\u0FB1]|[\u0F66][\u0FA3][\u0FB1]|[\u0F66][\u0F90\u0F92\u0FA3\u0FA4\u0FA6\u0FA8][\u0FAD]|[\u0F56][\u0F66][\u0FA4\u0FA6\u0FA8]|[\u0F56][\u0F66][\u0FA3][\u0FB2]|[\u0F56][\u0F66][\u0F90][\u0FB2][\u0FAD])[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,})"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' bad ya-rtags combos

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F43-\u0F53\u0F57\u0F59-\u0F68\u0F91\u0F93-\u0FA3\u0FA5\u0FA7\u0FA9-\u0FB8][\u0FB1][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' bad ra-rtags combos

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F43-\u0F50\u0F52\u0F53\u0F57\u0F59-\u0F65\u0F67\u0F68\u0F91\u0F93-\u0FA3\u0FA5\u0FA7\u0FA9-\u0FB8][\u0FB2][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' bad la-rtags combos

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F41\u0F43-\u0F50-\u0F55\u0F57-\u0F5E\u0F60\u0F61\u0F63-\u0F65\u0F67\u0F68\u0F90-\u0FB8][\u0FB3][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' bad wa-zur combos

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F41-\u0F44\u0F46-\u0F50\u0F52-\u0F61\u0F63-\u0F68\u0F90-\u0FA8\u0FAA-\u0FB1\u0FB3-\u0FB8][\u0FAD][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdYellow
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' FLAG "mantra stacks" in blue/turquoise
    ' Now do Sanskrit: Skt.vowels, [g|d|b|dz]+_h, hr, shr, Skt

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "([\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F71\u0F73\u0F75-\u0F79\u0F7B\u0F7D-\u0F83][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}|[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F42\u0F4C\u0F51\u0F56\u0F5B\u0F92\u0F9C\u0FA1\u0FA6\u0FAB][\u0FB7][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}|[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F64\u0F67][\u0FB2][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}|[\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F43\u0F4A-\u0F4E\u0F52\u0F57\u0F5C\u0F65\u0F69-\u0F6C\u0F71\u0F73\u0F75-\u0F79\u0F7B\u0F7D-\u0F83\u0F93\u0F9A-\u0F9E\u0FA2\u0FA7\u0FAC\u0FB5\u0FB9-\u0FBC][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,})"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdTurquoise
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord
    
    ' more Sanskrit: invalid superscript-subscript pairs

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "([\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F40\u0F41\u0F42\u0F44-\u0F49\u0F4F-\u0F51\u0F53-\u0F56\u0F58-\u0F5B\u0F5D-\u0F61\u0F64\u0F67\u0F68][\u0F90-\u0FAC\u0FAE-\u0FB0\u0FB4-\u0FBC][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,})"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdTurquoise
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord
    
    ' tsa-phru mark used in Chinese transliteration

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "([\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F39][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,})"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdTurquoise
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord
    
    ' FLAG "encoding errors" in red
    ' wrong ra used in ra-mgo combos; wrong subjoined 'a (a-chung)

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "([\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0FB0][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}|[\u0F56]?[\u0F6A][\u0F90\u0F92\u0F94\u0F97\u0F99\u0F9F\u0FA1\u0FA3\u0FA6\u0FA8\u0FA9\u0FAB\u0FAD\u0FB3][\u0F72\u0F74\u0F7A\u0F7C]?[\u0F42\u0F44\u0F51\u0F53\u0F56\u0F58\u0F60\u0F62\u0F63\u0F66]{0,})"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdRed
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' vowel after Skt punctuation

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "([\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,}[\u0F04-\u0F38\u0F7E\u0F7F\u0F82-\u0F8F\u0FBE-\u0FDA][\u0F72\u0F74\u0F7A\u0F7C\u0F71\u0F73\u0F75-\u0F79\u0F7B\u0F7D-\u0F83][\u0F40-\u0F6C\u0F71-\u0F85\u0F90-\u0FBC]{0,})"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdRed
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    ' PATCH to remove false positives

    Set tibetanLetterRegExp = New RegExp
    tibetanLetterRegExp.Pattern = "([\u0F42\u0F51\u0F56\u0F58\u0F60]?[\u0F40\u0F41\u0F42\u0F44-\u0F49\u0F4F-\u0F51\u0F53-\u0F56\u0F58-\u0F5B\u0F5D-\u0F64\u0F66-\u0F68][\u0FB1\u0FB2\u0FB3\u0FAD]?[\u0F72\u0F74\u0F7A\u0F7C]?[\u0F60][\u0F44\u0F58])"
    tibetanLetterRegExp.Global = True

    Set tibetanWrongWords = tibetanLetterRegExp.Execute(ActiveDocument.Range)

    For Each tibetanWrongWord In tibetanWrongWords

        Options.DefaultHighlightColorIndex = wdNoHighlight
        Selection.Find.ClearFormatting
        Selection.Find.Replacement.ClearFormatting
        Selection.Find.Replacement.Highlight = True
             With Selection.Find
            .Text = tibetanWrongWord
            .Replacement.Text = tibetanWrongWord
            .Forward = True
            .Wrap = wdFindContinue
            .Format = True
            .MatchCase = True
            .MatchWholeWord = False
            .MatchWildcards = False
            .MatchSoundsLike = False
            .MatchAllWordForms = False
        End With
        Selection.Find.Execute Replace:=wdReplaceAll

    Next tibetanWrongWord

    MsgBox "Tibetan Spell-checking Complete", vbOKOnly, "Alert"

End Sub

