Localisation
As of 2026-10-19 (vulture-ios 3176c4cc4, vulture-android 85fd9cc6, vulture-web 61d304e).
The iOS String Catalogs are the single source of the app’s text. Android generates its strings from them, so nothing is translated twice. For voice, tone, form of address and the locked glossary, see the Localization style guide (apps) and Tone of voice (website).
Languages
Section titled “Languages”Vulture ships 8 languages. Every catalogue is complete in all of them.
| Code | Language | iOS | Android (values-…) | Browser extensions (_locales) | vulture.aero (locales/) |
|---|---|---|---|---|---|
en | English (US, source) | Yes | values/ | Yes | Yes |
nl | Dutch | Yes | Yes | Yes | Yes |
de | German | Yes | Yes | Yes | Yes |
fr | French | Yes | Yes | Yes | Yes |
es | Spanish | Yes | Yes | Yes | Yes |
cs | Czech | Yes | Yes | Yes | Yes |
it | Italian | Yes | Yes | Yes | Yes |
pl | Polish | Yes | Yes | Yes | Yes |
Slovenian is not shipped. localization-style.md plans it: Slovenian is in its translation phase list and its form-of-address table, and the plural rules mention its dual form. No String Catalog has a sl localisation, and no Android values-sl, extension _locales/sl or site locale exists (all checked on 2026-10-19). That guide’s phase list also leaves out French, which ships. Treat the code inventory above as the truth.
iOS String Catalogs
Section titled “iOS String Catalogs”All catalogues are .xcstrings, with source language en.
| Catalogue | Keys | Used by |
|---|---|---|
vulture-ios/Packages/DesignSystem/Sources/DesignSystem/Resources/Localizable.xcstrings | 1006 | All the screen packages resolve through it |
vulture-ios/Modules/App/Resources/Localizable.xcstrings | 513 | The app target |
vulture-ios/Modules/App/Resources/AppShortcuts.xcstrings | 12 | App Shortcuts phrases |
vulture-ios/Modules/App/Resources/InfoPlist.xcstrings | 19 | Permission texts and other Info.plist strings |
vulture-ios/Modules/Watch/Resources/Localizable.xcstrings, InfoPlist.xcstrings | 134, 7 | Watch app |
vulture-ios/Modules/MetarWidget/Resources/Localizable.xcstrings | 46 | Widgets |
vulture-ios/Modules/QuickLook/Resources/Localizable.xcstrings | 18 | Quick Look preview |
vulture-ios/Modules/Clip/Resources/Localizable.xcstrings | 4 | App Clip |
vulture-ios/Packages/Preferences/.../Localizable.xcstrings | 39 | Units and preferences |
vulture-ios/Packages/Formatting/.../Localizable.xcstrings | 24 | Formatters |
vulture-ios/Packages/LiveActivity/.../Localizable.xcstrings | 6 | Live Activity |
vulture-ios/Packages/Waypoints/.../Localizable.xcstrings | 1 | Waypoints |
Key convention
Section titled “Key convention”- Most keys are dotted semantic keys, in the form
<area>.<kind>.<name>. Examples:common.button.ok,settings.labels.unit.kph,inflight.button.centerglider,file.label.import.success.title,skysight.layers.section.labels.thermals. - App Intents, App Shortcuts and widget strings use the English source text as the key. This is the system default for those APIs.
- Plurals use the catalogue’s “vary by plural”. Never write “item(s)” by hand.
- Unit abbreviations and aviation terms (METAR, TAF, QNH, AGL, FL) stay untranslated (style guide, “Do not translate”).
Android: generated from iOS
Section titled “Android: generated from iOS”The tooling is vulture-android/scripts/convert_strings.py, documented in vulture-android/docs/strings.md.
python3 scripts/convert_strings.py --ios ../vulture-ios # regeneratepython3 scripts/convert_strings.py --verify # completeness check only (CI)The script reads five catalogues: DesignSystem, App, Formatting, Preferences and Waypoints (the CATALOGS list). It writes vulture-android/core/designsystem/src/main/res/values*/strings_ios.xml. As of this date that file holds 1,526 strings and 22 plurals in English. The files start with a GENERATED comment. Never edit them. Change the iOS catalogue and regenerate, so both apps stay in sync.
| iOS | Android |
|---|---|
key flight.detail.title | R.string.flight_detail_title: lower-cased, non-[a-z0-9_] characters become _, repeated _ collapse, a leading digit gets an s_ prefix, reserved words get a trailing _ (resource_name()) |
%@ | %s |
%lld, %ld, %u, %i | %d |
| two or more placeholders | Positional: %1$s, %2$d |
| plural variation | <plurals>, read with pluralStringResource(R.plurals.x, count, count) |
| device variation | The iPhone variant |
stale keys, empty keys, keys that are only placeholders (%@ · %@) | Skipped |
R is be.fousa.vulture.core.designsystem.R, because all generated strings live in :core:designsystem.
ANDROID_WORDING overrides
Section titled “ANDROID_WORDING overrides”Since build 563, iOS writes menu commands and bar buttons in Apple HIG style: Title Case, with an ellipsis on a command that opens something to fill in (“Edit Flight…”). Material asks for sentence case and no ellipsis. The owner’s rule (2026-10-17, decision §17 in vulture-android/docs/decisions.md) is that the iOS HIG changes do not apply on Android.
ANDROID_WORDING in convert_strings.py maps resource names to their Android English text ("Edit flight"). It has 113 entries as of this date.
- Only the English
values/strings_ios.xmlis changed. The seven translations were already sentence case in the catalogue. - The script fails when an entry names a key the catalogue no longer has, so the table cannot go stale.
- Add an entry when a regeneration brings in a new Title Case button or menu string. Leave proper nouns (“Soaring Spot”, “Engine RPM”) alone.
Android-only strings
Section titled “Android-only strings”- Put them in the using module’s
res/values/strings.xml. Settings usesstrings_<area>.xml. Add all 8 locales (values-cs/…values-pl/). - Strings that must not be translated get
translatable="false". - A module with no Android-only strings has no
strings.xmlat all, not even an empty one (decision RA-18). - About 20 modules have their own strings files as of this date.
Known gaps in the conversion
Section titled “Known gaps in the conversion”- iOS substitutions (several plurals in one string) are converted without the plural logic. The script warns about each one. There are none today.
- Android uses the
zeroplural form only in languages that have it grammatically. iOS uses it for 0 in every language. Checkcount == 0in code where the zero text matters. - Two iOS keys can map to the same Android name (
flight.not.foundandFlight not found.). The script keeps the first and warns.
Adding or changing a string
Section titled “Adding or changing a string”| Change | Do |
|---|---|
| Text in both apps | Edit the iOS catalogue (all 8 languages), commit in vulture-ios, then regenerate in vulture-android |
| A new iOS menu or button in Title Case | Also add an ANDROID_WORDING entry with its sentence-case English |
| Android-only text | The module’s strings.xml in all 8 locales |
| Website text | vulture-web/locales/<lang>.yml, following Tone of voice |
| Browser extension text | vulture-ios/Chrome/_locales/ and vulture-ios/Modules/Safari/Resources/_locales/ |
