croatian.go raw

   1  package shadow
   2  
   3  import (
   4  	"strings"
   5  	"unicode/utf8"
   6  )
   7  
   8  // Croatian digraphs — must be checked before single letters.
   9  // Order matters: longer sequences first.
  10  var croatianDigraphs = []struct {
  11  	src string
  12  	dst string
  13  }{
  14  	{"dž", Dzh},
  15  	{"Dž", Dzh},
  16  	{"DŽ", Dzh},
  17  	{"lj", L + Y},
  18  	{"Lj", L + Y},
  19  	{"LJ", L + Y},
  20  	{"nj", N + Y},
  21  	{"Nj", N + Y},
  22  	{"NJ", N + Y},
  23  }
  24  
  25  // Croatian single-letter map. Every Croatian letter that differs from
  26  // its Shadow equivalent is listed here. Letters that map to themselves
  27  // (A→A, B→B, etc.) are handled by the default passthrough.
  28  var croatianSingles = map[rune]string{
  29  	// Letters that change
  30  	'c': T + S, 'C': T + S, // Croatian C = /ts/
  31  	'č': Ch, 'Č': Ch,
  32  	'ć': Ch, 'Ć': Ch, // Ć ≈ Č (palatal affricate, close enough)
  33  	'đ': Dzh, 'Đ': Dzh, // Đ ≈ Џ (palatal stop, close enough)
  34  	'š': Sh, 'Š': Sh,
  35  	'ž': Zh, 'Ž': Zh,
  36  	'j': Y, 'J': Y,
  37  
  38  	// Passthrough — identical sound, same letter
  39  	'a': A, 'A': A,
  40  	'b': B, 'B': B,
  41  	'd': D, 'D': D,
  42  	'e': E, 'E': E,
  43  	'f': F, 'F': F,
  44  	'g': G, 'G': G,
  45  	'h': H, 'H': H,
  46  	'i': I, 'I': I,
  47  	'k': K, 'K': K,
  48  	'l': L, 'L': L,
  49  	'm': M, 'M': M,
  50  	'n': N, 'N': N,
  51  	'o': O, 'O': O,
  52  	'p': P, 'P': P,
  53  	'r': R, 'R': R,
  54  	's': S, 'S': S,
  55  	't': T, 'T': T,
  56  	'u': U, 'U': U,
  57  	'v': V, 'V': V,
  58  	'z': Z, 'Z': Z,
  59  	'w': W, 'W': W,
  60  	'y': Y, 'Y': Y,
  61  }
  62  
  63  // FromCroatian transliterates Croatian (Gaj's Latin) text into the
  64  // Shadow Alphabet. Digits and whitespace pass through unchanged.
  65  // Unknown characters are preserved as-is.
  66  func FromCroatian(text string) string {
  67  	var out strings.Builder
  68  	out.Grow(len(text))
  69  
  70  	i := 0
  71  	for i < len(text) {
  72  		// Try digraphs first (longest match)
  73  		matched := false
  74  		for _, dg := range croatianDigraphs {
  75  			if strings.HasPrefix(text[i:], dg.src) {
  76  				out.WriteString(dg.dst)
  77  				i += len(dg.src)
  78  				matched = true
  79  				break
  80  			}
  81  		}
  82  		if matched {
  83  			continue
  84  		}
  85  
  86  		// Single character
  87  		r, size := utf8.DecodeRuneInString(text[i:])
  88  		if r == utf8.RuneError {
  89  			i++
  90  			continue
  91  		}
  92  
  93  		if s, ok := croatianSingles[r]; ok {
  94  			out.WriteString(s)
  95  		} else {
  96  			// Digits, spaces, newlines, unknown — passthrough
  97  			out.WriteRune(r)
  98  		}
  99  		i += size
 100  	}
 101  
 102  	return out.String()
 103  }
 104