With expl3
it's quite easy (and there's no limit on numbers, other than the usual 2147483647, the maximum number in TeX)
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\basetwelve}{m}
{ \duodec_convert:n { #1 } }
\tl_new:N \l_duodec_string_tl
\cs_new_protected:Npn \duodec_convert:n #1
{
\tl_set:Nx \l_duodec_string_tl { \int_to_base:nn { #1 } { 12 } }
\tl_replace_all:Nnn \l_duodec_string_tl { A } { $\odot$ }
\tl_replace_all:Nnn \l_duodec_string_tl { B } { $\Delta$ }
\tl_use:N \l_duodec_string_tl
}
\ExplSyntaxOff
\renewcommand{\thepage}{\basetwelve{\arabic{page}}}
\begin{document}
\ExplSyntaxOn
\int_step_inline:nnnn { 1 } { 1 } { 145 }
{ \basetwelve{#1} ~ }
\ExplSyntaxOff
\end{document}
The code is in the preamble and after it is a test for showing that the numbers are correctly produced in the requested form.

The \int_to_base:nn
function produces A
for 10 and B
for 11, so we have to replace the letters with the symbols.
The input \basetwelve{123456}
produces 5∆540.
What do we do? We define a robust command, \basetwelve
, that will wrap the call to the conversion, performed by \duodec_convert:n
. This command takes as input something that ultimately expands to a decimal number (for instance \arabic{page}
and stores away the number converted into duodecimal form, with digits 0123456789AB, through the function \int_to_base:nn
. Then we replace all A's with $\odot$
and all B's with $\Delta$
and deliver the so converted token list.
When LaTeX wants to write a page number to the .aux
file, it will expand \thepage
, getting \basetwelve{\arabic{page}}
. Since \basetwelve
is robust (all commands defined with \NewDocumentCommand
are), it will be written untouched. Instead \arabic{page}
will be expanded to the current page number, so what's written in the .aux
file is, for instance, \basetwelve{23}
. When this is used by \pageref
the conversion will be performed, giving 1∆.
dozenal
package on CTAN?