Every user-facing string under src/views/Authorization/ moves to i18next.
English only — no translations in this PR, so it is language-agnostic and
reviewable as a pure refactor.
Covered:
- LoginView — primary/sub-account tabs, credential form, social and
Authentik buttons, welcome-back state, every auth error toast
- Signup — account creation form and each field-validation message
- ForgotPasswordView / UpdatePasswordView — reset flow and its toasts
- Authenticating — the OAuth landing screen, including its MFA branches
- MFAVerificationModal — code entry, backup codes, error states
- AuthFields / LoginSettings — shared field labels and server settings
`auth` is registered in src/i18n/config.js; public/locales/en/auth.json
holds the 103 keys. The Crowdin config picks up /public/locales/en/*.json
by glob, so the namespace flows into the pipeline with no change to
crowdin.yml.
No behaviour change: every t() value is the string that rendered before,
character for character — checked mechanically against this branch's base.
The e2e suite selects auth controls by visible text ('Create account',
'Username must be at least 4 characters'), so a green run is the proof.
Two fixes fell out of the extraction:
- AuthPasswordField had label='Password' as a prop default, so neither
LoginView nor Signup passed one. A default cannot be translated at module
scope, so the label moves to the call sites and both now pass it.
- AuthDivider defaulted children to 'or' for the same reason; it now falls
back to t('or').
Internationalization (i18n) Setup
This directory contains the internationalization configuration for Donetick.
Structure
src/i18n/
├── config.js # i18next configuration
└── README.md # This file
public/locales/
├── en/ # English (default)
│ ├── common.json
│ ├── settings.json
│ └── chores.json
├── es/ # Spanish
├── ar/ # Arabic (RTL)
└── ...
Usage in Components
Using translations
import { useTranslation } from 'react-i18next'
function MyComponent() {
const { t } = useTranslation('settings') // or 'common', 'chores'
return <h1>{t('title')}</h1>
}
Using date formatting
import { useLocalization } from '@/contexts/LocalizationContext'
function MyComponent() {
const { formatDate, formatDateTime, formatRelative } = useLocalization()
const date = new Date()
return (
<div>
<p>Date: {formatDate(date)}</p>
<p>DateTime: {formatDateTime(date)}</p>
<p>Relative: {formatRelative(date)}</p>
</div>
)
}
Using language/format settings
import { useLocalization } from '@/contexts/LocalizationContext'
function MyComponent() {
const {
language,
setLanguage,
dateFormat,
setDateFormat,
isRTL
} = useLocalization()
return (
<div dir={isRTL ? 'rtl' : 'ltr'}>
Current language: {language}
</div>
)
}
Available Namespaces
- common: General UI elements (buttons, messages, etc.)
- settings: Settings page translations
- chores: Chores-related translations
Adding New Translations
- Add the text to the appropriate JSON file in
public/locales/en/ - Use the translation in your component with
t('key') - Upload to translation platform for community translation
RTL Support
Languages in the RTL_LANGUAGES array automatically get:
dir="rtl"on the document- RTL-specific CSS styles
- Proper text alignment
Currently supported RTL languages: Arabic (ar), Hebrew (he), Persian (fa), Urdu (ur)
Date Format Preferences
Users can choose from:
- MM/DD/YYYY (US)
- DD/MM/YYYY (Europe)
- YYYY-MM-DD (ISO)
- Long format (January 1, 2024)
- Short format (Jan 1, 2024)
Time Format Preferences
- 12-hour (with AM/PM)
- 24-hour
First Day of Week
Users can choose:
- Sunday
- Monday