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