Build apps with i18n
lib/i18n.ts wires i18next with expo-localization for automatic locale detection and JSON translation files generated per locale. Goodspeed generates i18n as a standard part of every app, so the output is a working codebase from day one, not a scaffold you have to finish yourself.
WHAT GETS GENERATED
Built into every i18n build
Every app Goodspeed generates with i18n includes these production-ready patterns, wired together from the first build.
| Item | Description | Strength |
|---|---|---|
| lib/i18n.ts with expo-localization | getLocales() reads the device's preferred language. The detected language is matched against gasConfig.features.i18n.locales; unrecognized locales fall back to defaultLocale. | Detection |
| locales/ directory with JSON translation files | The DevAgent generates locales/en.json and one file per declared locale. Each file has the same key set so missing translations surface as runtime warnings. | Translations |
| gasConfig.features.i18n feature flag | When gasConfig.features.i18n.enabled is false, i18next is not initialized and t() returns the key string as a passthrough, keeping single-locale apps lightweight. | Feature Flag |
| t() and changeLanguage() exports | lib/i18n.ts re-exports t from i18next and a typed changeLanguage(locale) helper. Components import from lib/i18n rather than i18next directly for consistent error handling. | API |
| Conditional i18n CI workflow | .github/workflows/i18n.yml runs a translation-completeness check on PRs, failing if any declared locale is missing a key present in en.json. | CI |
Source: the Goodspeed generation pipeline · Internationalization
REAL GENERATED CODE
A snippet from a i18n app the studio shipped
This pattern comes straight out of the production groundwork every Goodspeed app is generated on. The studio generates i18n code like this for every app in the pipeline, not a hello-world sample you have to grow into a real app.
Locale detection
// lib/i18n.ts: expo-localization locale detection function getDeviceLocale(): string { try { const locales = getLocales(); const deviceLang = locales[0]?.languageCode ?? i18nConfig.defaultLocale; if (i18nConfig.locales.includes(deviceLang)) { return deviceLang; } return i18nConfig.defaultLocale; } catch { return i18nConfig.defaultLocale; } }
Today's log