i18n Rules
Every user-facing string in RNCopilot goes through the internationalization system. The project ships with English and Arabic (with full RTL support). This page covers the rules for translation keys, locale files, and RTL handling.
The Core Rule
No hardcoded strings in JSX or component logic. This is a hard rule with no exceptions.
// CORRECT
const { t } = useTranslation();
<Text>{t('home.welcome')}</Text>
// WRONG
<Text>Welcome to the app!</Text>Translation Hook
Use the useTranslation() hook from react-i18next in every component that renders text:
import { useTranslation } from 'react-i18next';
export function MyComponent() {
const { t } = useTranslation();
return (
<View>
<Text>{t('myFeature.title')}</Text>
<Button title={t('actions.submit')} onPress={handleSubmit} />
</View>
);
}Locale Files
Both locale files must be updated together. When you add a key to English, add the corresponding key to Arabic in the same commit.
| File | Language | Direction |
|---|---|---|
src/i18n/locales/en.json | English (primary) | LTR |
src/i18n/locales/ar.json | Arabic | RTL |
Key Naming
Keys use dot notation and semantic naming. Group keys by feature or purpose:
{
"home": {
"welcome": "Welcome",
"subtitle": "Get started below"
},
"auth": {
"login": "Sign In",
"logout": "Sign Out",
"email": "Email address",
"password": "Password"
},
"actions": {
"submit": "Submit",
"cancel": "Cancel",
"save": "Save",
"delete": "Delete"
},
"validation": {
"required": "This field is required",
"emailInvalid": "Enter a valid email address",
"passwordMin": "Password must be at least 8 characters"
},
"errors": {
"generic": "Something went wrong. Please try again.",
"network": "Network error. Check your connection."
}
}Zod Validation Messages
Zod schema error messages are i18n keys, not raw text. The form system resolves them through the translation function:
import { z } from 'zod/v4';
export const loginSchema = z.object({
email: z.email('validation.emailInvalid'),
password: z.string().min(8, 'validation.passwordMin'),
});Validation messages in Zod schemas are translation keys (e.g., 'validation.required'), not the final user-visible strings.
RTL Support
RTL layout mirroring is handled globally at the app level in app/_layout.tsx. You do not need to manually flip layouts in individual components.
- Use
flexDirectionvalues normally — the RTL system mirrors them automatically. - Do not conditionally swap
paddingLeft/paddingRightbased on language. - Do not use
I18nManager.forceRTL()in component code — it is managed in the root layout.
Adding a New Language
To add a new locale:
- Create a new JSON file in
src/i18n/locales/(e.g.,fr.json). - Copy the structure from
en.jsonand translate all values. - Register the new locale in the i18n configuration at
src/i18n/. - If the language is RTL, add it to the RTL language list in the configuration.