State of work before Duo-style onboarding pivot. Includes work that will be partly reverted in the next commit (see refactor follow-up). Onboarding (will be partly reverted): - Custom Tooltip+Glow spotlight (components/OnboardingHint.tsx) - Spotlight wiring in app/profile/edit.tsx (nickname-input glow + step-progress header, onSubmitEditing auto-save, save-handler routes to /(app)/blocker) - Spotlight wiring in app/(app)/blocker.tsx (URL-filter LayerSwitchCard wrapped + auto-PATCH step='done' when filter activates) - Routing-gate branches in (app)/_layout.tsx (welcome → /onboarding/welcome, nickname → /profile/edit) - Debug-Reset-Toggle in /debug (welcome|nickname|block|done buttons + redirect) Will stay (reused in Duo flow): - Welcome-Screen app/onboarding/welcome.tsx (will become Slide 1) - Avatar-fix in profile/edit (Dicebear seed stays stable while typing) i18n + RTL: - Arabic locale (locales/ar.json, full translation incl. onboarding keys) - I18nManager.allowRTL(true) + applyRTL helper in stores/language.ts - Language-Picker option for العربية in settings - New keys: onboarding.welcome.*, step_progress, nickname_spotlight.*, block_spotlight.*, permission_denied.*, language.*, rtl_restart.* (de/en/fr/ar) NEFilter Permission Recovery (iOS): - Swift resetUrlFilter() — removeFromPreferences + fresh saveToPreferences to bypass iOS's cached denied-state (NEFilterErrorDomain code 5) - TS module def + lib/protection.ts wrapper - components/PermissionDeniedSheet.tsx — branded recovery sheet with retry + app-settings:// deep-link + fallback hint - Wired in (app)/blocker.tsx handleActivateUrlFilter (code-5 detection) Misc: - Bug fix in onboarding/welcome.tsx: apiFetch body was double-stringified (sent as JSON string instead of object → 400 invalid_step) - Bug fix in profile/edit.tsx: avatar preview Dicebear seed switched from live nickname (changed every keystroke) to stable me?.nickname Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
39 lines
1.0 KiB
TypeScript
39 lines
1.0 KiB
TypeScript
import i18n from 'i18next';
|
|
import { initReactI18next } from 'react-i18next';
|
|
import * as Localization from 'expo-localization';
|
|
import de from '../locales/de.json';
|
|
import en from '../locales/en.json';
|
|
import fr from '../locales/fr.json';
|
|
import ar from '../locales/ar.json';
|
|
|
|
const deviceLocale = Localization.getLocales()[0]?.languageCode ?? 'en';
|
|
const initialLng =
|
|
deviceLocale === 'de'
|
|
? 'de'
|
|
: deviceLocale === 'fr'
|
|
? 'fr'
|
|
: deviceLocale === 'ar'
|
|
? 'ar'
|
|
: 'en';
|
|
|
|
i18n.use(initReactI18next).init({
|
|
resources: {
|
|
de: { translation: de },
|
|
en: { translation: en },
|
|
fr: { translation: fr },
|
|
ar: { translation: ar },
|
|
},
|
|
lng: initialLng,
|
|
fallbackLng: 'en',
|
|
// RN hat kein eingebautes Intl.PluralRules — v3 funktioniert nativ ohne Polyfill
|
|
compatibilityJSON: 'v3',
|
|
interpolation: {
|
|
escapeValue: false,
|
|
// Locale-Dateien verwenden Vue-i18n-Style %{var} (1:1 portiert aus der Nuxt-App).
|
|
prefix: '%{',
|
|
suffix: '}',
|
|
},
|
|
});
|
|
|
|
export default i18n;
|