For completeness, while case has a | OR operator, it doesn't have an AND operator but if using shells with extended glob operators (ksh, zsh, bash), you can implement the AND in the pattern syntax:
ksh93¹'s@(x&y&z)operator:case $string in ( @({12}(?)&~(i:[aeiou]*)&*[0123456789]) ) echo is 12 characters long AND starts with a vowel AND ends in a decimal esaczsh(using~(AND-NOT) combined with^(NOT)):x~^y~^zset -o extendedglob case $string in ( ?(#c12)~^(#i)[aeiou]*~^*[0-9] ) echo is 12 characters long AND starts with a vowel AND ends in a decimal esacksh88+¹ or bash, using double negation with OR (!(!(x)|!(y)|!(z)))shopt -s extglob # bash only case $string in ( !(!(????????????)|!([aAeEıiIİoOuU]*)|!(*[0123456789])) ) echo is 12 characters long AND starts with a vowel AND ends in a decimal esac
In any case, remember that except in zsh where ranges are always based on codepoint values, ranges like [0-9] cannot be used reliably outside of the POSIX/C locale (hence the [0123456789] instead above).
ksh93 and zsh's case insensitive matching operators (~(i) and (#i)) honour the locale for case sensitive comparison. For instance, in a Turkish locale on a GNU system, (#i)[aeiou] will match on İ, but not I (because uppercase i is İ there). To get a consistent outcome regardless of the locale, you may want to hard code all possible values instead like in the ksh88/bash approach.
¹ Beware that ksh88 and older versions of ksh93 had that misfeature by which if a string didn't match a pattern, case would fall back to do a literal match. For instance, the @(x|y) pattern matches on both x and y but also on @(xx|y) there!