One approach for a Python implementation would be to store strings as UTF-8, and generate an index array as needed. For most operations, you don't need an index. Concatenation doesn't need one. Ordinary iteration (for c in "hello" :) doesn't need one. Regular expressions don't need an index. That covers most of the common use cases.
I like "rune" as terminology. A rune is one Unicode code point. A grapheme is a sequence of runes which should not be split.