This PR contains the work done to do a little bit of housekeeping pass across all packages and the Website service. To provide further details about the work: * Refreshed the READMEs and source documentation to match the current code; * Tagged every test case consistently across the Infrastructure, Localization, Persistence, and Website test targets; * Removed Website middleware tests now covered by Infrastructure's own suite; * Conformed the `PrepareDB` method to Sendable; * Relaxes the production Compose DATABASE_TLS default from require to prefer; * Added Persistence test verifying the prefer posture falls back to plaintext connections. Reviewed-on: rock-n-code/loud-amsterdam#27 Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
45 lines
3.6 KiB
Markdown
45 lines
3.6 KiB
Markdown
# Localization
|
|
The server-side localization toolkit the **Loud** services build on: locale-explicit String Catalog lookups, `Accept-Language` negotiation, and the catalog-derived language list — with no dependencies beyond Foundation.
|
|
|
|
## Overview
|
|
The package provides, grouped by role:
|
|
| Role | Types |
|
|
| --- | --- |
|
|
| Lookup | `Localize`, a bundle-bound localizer that resolves a catalog key for an explicit locale |
|
|
| Negotiation | `Negotiate`, which picks the best supported language from an `Accept-Language` header per RFC 9110 |
|
|
| Languages | `LanguageList`, the supported and default languages a bundle's String Catalog defines |
|
|
| Diagnostics | `CatalogState`, the outcome of reading the catalog (`loaded`, `missing`, or `undecodable`) |
|
|
|
|
## Design rules
|
|
- **The String Catalog is the single source of truth.** Supported languages, the default language, and every string come from the bundle's `Localizable.xcstrings`. Adding a language is a translation-only change — once a locale exists in the catalog, `LanguageList` and `Negotiate` pick it up with no code change.
|
|
- **The locale is always explicit.** A server has no single "current" locale, so every lookup names the locale to resolve in; nothing reads process-wide locale state.
|
|
- **Raw `.xcstrings` parsing, for Linux parity.** The catalog is decoded from its JSON rather than through Foundation's compiled-catalog APIs, which are unavailable or non-functional on Linux. Consumers must `.copy` the catalog resource verbatim (not `.process` it) so it ships as raw JSON on every platform. Only simple `stringUnit` values are decoded; plural and device variations are not represented.
|
|
- **Resolution never fails.** A missing entry falls back to the source-language string, then to the key itself; a missing or undecodable catalog degrades the language list to the default. Check `catalogState` once at startup and warn when it is not `.loaded`, before visitors ever see raw keys.
|
|
- **Method structs.** `Localize` and `Negotiate` hold their lifetime-fixed configuration (the bundle) in `init` and take only per-call inputs in `callAsFunction`.
|
|
- **One decoded catalog per bundle.** Catalogs are immutable at runtime, so every `Localize`, `Negotiate`, and `LanguageList` bound to the same bundle and table shares one cached `StringCatalog`.
|
|
|
|
## Layout
|
|
Sources are split by visibility, then by kind, one type per file:
|
|
```
|
|
Sources/
|
|
├── Public/
|
|
│ ├── Enumerations/ CatalogState
|
|
│ ├── Methods/ Localize, Negotiate
|
|
│ └── Types/ LanguageList
|
|
└── Internal/
|
|
├── Protocols/ CatalogResolving, the seam between the public API and the catalog backend
|
|
└── Types/ StringCatalog (the cached .xcstrings decoder), LanguageRange
|
|
Tests/
|
|
├── Cases/ the test suites, mirroring the Sources/ layout
|
|
├── Catalogs/ the String Catalog fixture, copied verbatim so it loads on Linux
|
|
└── Utils/ the StubCatalog resolver and the suite Tag constants
|
|
```
|
|
|
|
## Testing
|
|
Every suite carries a tag naming the kind of API it exercises — `.method` or `.type`, declared in `Tests/Utils/Extensions/Tag+Constants.swift` — so test plans and result summaries can slice the run by kind. A new suite must adopt the tag matching its subject (or add a tag there if none fits).
|
|
|
|
## Requirements
|
|
- Swift 6.3 toolchain (`swift-tools-version:6.3`).
|
|
- macOS 15, matching the sibling `Infrastructure` and `Persistence` packages (the services deploy to Linux containers; the packages carry no UI platforms).
|
|
- No package dependencies — Foundation and Synchronization only.
|