|
| 1 | +# LOL i18n Service — ABI/FFI/API V-Triple Documentation |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +The LOL (Language of Languages) i18n service follows the **Hyperpolymath V-Triple Standard**: |
| 6 | + |
| 7 | +- **ABI (Application Binary Interface)** defined in **Idris2** with formal proofs |
| 8 | +- **FFI (Foreign Function Interface)** implemented in **Zig** for C compatibility |
| 9 | +- **API** exposed in **V-lang** for idiomatic high-level access |
| 10 | + |
| 11 | +LOL is always called as a service, never embedded. Consumers initialise with |
| 12 | +`lol_init()`, perform translation lookups and plural form selection across |
| 13 | +1500+ languages, then clean up with `lol_free()`. |
| 14 | + |
| 15 | +## Architecture |
| 16 | + |
| 17 | +``` |
| 18 | +┌─────────────────────────────────────────────────┐ |
| 19 | +│ ABI Definitions (Idris2) │ |
| 20 | +│ src/abi/ │ |
| 21 | +│ - Types.idr (Result, Locale, PluralCategory,│ |
| 22 | +│ LanguageInfo, TranslationResult)│ |
| 23 | +│ - Layout.idr (Memory layout proofs for each │ |
| 24 | +│ C struct that crosses the FFI) │ |
| 25 | +│ - Foreign.idr (FFI function declarations with │ |
| 26 | +│ safe wrappers) │ |
| 27 | +└─────────────────┬───────────────────────────────┘ |
| 28 | + │ |
| 29 | + │ generates |
| 30 | + ▼ |
| 31 | +┌─────────────────────────────────────────────────┐ |
| 32 | +│ C Headers (auto-generated) │ |
| 33 | +│ generated/abi/lol.h │ |
| 34 | +│ - lol_result_t, lol_plural_category_t │ |
| 35 | +│ - lol_locale_t, lol_translation_result_t │ |
| 36 | +│ - lol_language_info_t, lol_plural_rule_t │ |
| 37 | +│ - All function prototypes │ |
| 38 | +└─────────────────┬───────────────────────────────┘ |
| 39 | + │ |
| 40 | + │ implemented by |
| 41 | + ▼ |
| 42 | +┌─────────────────────────────────────────────────┐ |
| 43 | +│ FFI Implementation (Zig) │ |
| 44 | +│ ffi/zig/src/main.zig │ |
| 45 | +│ - BCP 47 locale parsing │ |
| 46 | +│ - CLDR plural rule engine │ |
| 47 | +│ - Corpus data directory access │ |
| 48 | +│ - Translation lookup with fallback chains │ |
| 49 | +│ - Thread-safe error handling │ |
| 50 | +└─────────────────┬───────────────────────────────┘ |
| 51 | + │ |
| 52 | + │ linked by (-llol) |
| 53 | + ▼ |
| 54 | +┌─────────────────────────────────────────────────┐ |
| 55 | +│ V-lang API (idiomatic wrapper) │ |
| 56 | +│ api/v-lol/src/ │ |
| 57 | +│ - lol.v (Service struct, public methods) │ |
| 58 | +│ - ffi.v (Raw C bindings) │ |
| 59 | +│ - types.v (V types: Locale, PluralCategory, │ |
| 60 | +│ TranslationResult, LanguageInfo) │ |
| 61 | +└─────────────────────────────────────────────────┘ |
| 62 | +``` |
| 63 | + |
| 64 | +## Directory Structure |
| 65 | + |
| 66 | +``` |
| 67 | +lol/ |
| 68 | +├── src/ |
| 69 | +│ └── abi/ # ABI definitions (Idris2) |
| 70 | +│ ├── Types.idr # Core types with formal proofs |
| 71 | +│ ├── Layout.idr # Memory layout verification |
| 72 | +│ └── Foreign.idr # FFI function declarations |
| 73 | +├── ffi/ |
| 74 | +│ └── zig/ # FFI implementation (Zig) |
| 75 | +│ ├── build.zig # Build configuration |
| 76 | +│ ├── src/main.zig # Implementation |
| 77 | +│ └── test/integration_test.zig# Integration tests |
| 78 | +├── generated/ |
| 79 | +│ └── abi/ |
| 80 | +│ └── lol.h # Auto-generated C header |
| 81 | +├── api/ |
| 82 | +│ ├── v-lol/ # V-lang API wrapper |
| 83 | +│ │ ├── v.mod # V module definition |
| 84 | +│ │ └── src/ |
| 85 | +│ │ ├── lol.v # Public API (Service struct) |
| 86 | +│ │ ├── ffi.v # Raw C bindings |
| 87 | +│ │ └── types.v # V types and enums |
| 88 | +│ └── v-gateway/ # Existing triple API gateway |
| 89 | +│ └── src/ # REST + gRPC + GraphQL |
| 90 | +└── ABI-FFI-README.md # This file |
| 91 | +``` |
| 92 | + |
| 93 | +## Key Types |
| 94 | + |
| 95 | +| Type | Idris2 | C | V | |
| 96 | +|------|--------|---|---| |
| 97 | +| Result codes | `Result` | `lol_result_t` | `LolError` | |
| 98 | +| Plural category | `PluralCategory` | `lol_plural_category_t` | `PluralCategory` | |
| 99 | +| Locale | `Locale` | `lol_locale_t` | `Locale` | |
| 100 | +| Translation | `TranslationResult` | `lol_translation_result_t` | `TranslationResult` | |
| 101 | +| Language info | `LanguageInfo` | `lol_language_info_t` | `LanguageInfo` | |
| 102 | +| Plural rule | `PluralRule` | `lol_plural_rule_t` | `PluralRule` | |
| 103 | + |
| 104 | +## Formal Proofs (Idris2 ABI) |
| 105 | + |
| 106 | +The Idris2 ABI layer provides the following compile-time guarantees: |
| 107 | + |
| 108 | +1. **Result code round-trip**: `resultFromInt (resultToInt r) = Just r` for all `r` |
| 109 | +2. **Plural category round-trip**: `pluralFromInt (pluralToInt p) = Just p` for all `p` |
| 110 | +3. **Result code injectivity**: distinct results map to distinct integer codes |
| 111 | +4. **Handle non-null**: the `Handle` type cannot wrap a null pointer (enforced by `So`) |
| 112 | +5. **Struct alignment**: all C struct layouts are proven to have correctly aligned fields |
| 113 | +6. **Plural form count bounds**: `PluralRule.formCount` is proven to be in range 1-6 |
| 114 | + |
| 115 | +No `believe_me`, `assert_total`, or other escape hatches are used. |
| 116 | + |
| 117 | +## Building |
| 118 | + |
| 119 | +### Zig FFI |
| 120 | + |
| 121 | +```bash |
| 122 | +cd ffi/zig |
| 123 | +zig build # Builds liblol.so and liblol.a |
| 124 | +zig build test # Runs unit tests |
| 125 | +zig build test-integration # Runs integration tests |
| 126 | +``` |
| 127 | + |
| 128 | +### V-lang API |
| 129 | + |
| 130 | +```bash |
| 131 | +cd api/v-lol |
| 132 | +v -cflags "-L../../ffi/zig/zig-out/lib" src/ # Build with liblol |
| 133 | +``` |
| 134 | + |
| 135 | +## Usage (V-lang) |
| 136 | + |
| 137 | +```v |
| 138 | +import lol |
| 139 | +
|
| 140 | +fn main() { |
| 141 | + // Open the service with a corpus data directory |
| 142 | + mut svc := lol.open('/path/to/corpus') or { panic(err) } |
| 143 | + defer svc.close() |
| 144 | +
|
| 145 | + // Translate a key |
| 146 | + result := svc.translate('en-US', 'app.greeting') or { |
| 147 | + println('Translation not found, using default') |
| 148 | + lol.TranslationResult{ text: 'Hello!', resolved_locale: 'en' } |
| 149 | + } |
| 150 | + println(result.text) |
| 151 | +
|
| 152 | + // Select plural form |
| 153 | + cat := svc.select_plural('ar', 5) |
| 154 | + println('Arabic plural for 5: ${cat}') // "few" |
| 155 | +
|
| 156 | + // Get language metadata |
| 157 | + info := svc.get_language('eng') or { panic(err) } |
| 158 | + println('${info.name} (${info.native_name}): ${info.verse_count} verses') |
| 159 | +
|
| 160 | + // Get plural rule |
| 161 | + rule := svc.get_plural_rule('ru') or { panic(err) } |
| 162 | + println('Russian has ${rule.form_count} plural forms') |
| 163 | +} |
| 164 | +``` |
| 165 | + |
| 166 | +## License |
| 167 | + |
| 168 | +SPDX-License-Identifier: PMPL-1.0-or-later |
| 169 | + |
| 170 | +Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) |
0 commit comments