Use a non-word boundary:
result = result.replaceAll("\\B0|0\\B", "o");
That ensures there is at least one word character before or after the 0.
If you want to prevents zero inside a number to be replaced:
result = result.replaceAll("\\b(?!\\d+\\b)(?:0\\B|([^\\W0]+)0)|\\G(?!\\A)0", "$1o");
details:
\\b # a word boundary
(?!\\d+\\b) # negative lookahead: not followed by an integer
(?:
0\\B # zero and a non-word boundary (means a word character follows)
|
([^\\W0]+)0 # word characters without zero and a zero
)
|
\\G(?!\\A)0 # a zero contiguous to a previous match (not at the start of the string)
(obviously a regex pattern can't make the difference between an isolated "0" and an isolated "o", or between a "0" and a "o" in a reference number, or a number in scientific notation)
other way: capturing all the opponents
result = result.replaceAll("((?>(?:[\\W_]+|\\pL+|\\b\\d+\\b)*))(?:\\B0|0\\B)", "$1o");