Add a New Language

Step-by-step guide to add a new language to your app.

Step 1: Create Translation File

Create a new JSON file for your language:

bash
# Create French translation file
touch src/i18n/locales/fr.json
src/i18n/locales/fr.json
{
  "common": {
    "welcome": "Bienvenue",
    "loading": "Chargement...",
    "error": "Une erreur s'est produite",
    "save": "Enregistrer",
    "cancel": "Annuler",
    "delete": "Supprimer",
    "edit": "Modifier"
  },
  "auth": {
    "login": "Connexion",
    "logout": "Déconnexion",
    "email": "E-mail",
    "password": "Mot de passe",
    "forgotPassword": "Mot de passe oublié ?",
    "noAccount": "Vous n'avez pas de compte ?",
    "signUp": "S'inscrire"
  },
  "home": {
    "title": "Accueil",
    "greeting": "Bonjour, {{name}} !",
    "taskCount": "Vous avez {{count}} tâche",
    "taskCount_plural": "Vous avez {{count}} tâches"
  }
}
Make sure all keys match your existing language files!

Step 2: Register Language

Add the language to i18n configuration:

src/i18n/index.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import * as Localization from 'expo-localization';

// Import all language files
import en from './locales/en.json';
import pt from './locales/pt.json';
import es from './locales/es.json';
import fr from './locales/fr.json'; // ✅ Add this

const resources = {
  en: { translation: en },
  pt: { translation: pt },
  es: { translation: es },
  fr: { translation: fr }, // ✅ Add this
};

i18n
  .use(initReactI18next)
  .init({
    resources,
    lng: Localization.locale.split('-')[0],
    fallbackLng: 'en',
    interpolation: {
      escapeValue: false,
    },
  });

export default i18n;

Step 3: Update Language Selector

Add the language option to your UI:

typescript
const languages = [
  { code: 'en', name: 'English', flag: '🇺🇸' },
  { code: 'pt', name: 'Português', flag: '🇧🇷' },
  { code: 'es', name: 'Español', flag: '🇪🇸' },
  { code: 'fr', name: 'Français', flag: '🇫🇷' }, // ✅ Add this
];

export function LanguageSelector() {
  const { i18n } = useTranslation();

  return (
    <View className="gap-2">
      {languages.map((lang) => (
        <Pressable
          key={lang.code}
          onPress={() => i18n.changeLanguage(lang.code)}
          className={cn(
            'p-4 rounded-lg flex-row items-center gap-3',
            i18n.language === lang.code
              ? 'bg-blue-500'
              : 'bg-slate-200 dark:bg-slate-700'
          )}
        >
          <Text className="text-2xl">{lang.flag}</Text>
          <Text
            className={cn(
              'font-semibold',
              i18n.language === lang.code
                ? 'text-white'
                : 'text-slate-900 dark:text-white'
            )}
          >
            {lang.name}
          </Text>
        </Pressable>
      ))}
    </View>
  );
}

Step 4: Add Date Locale (Optional)

For date formatting with date-fns:

bash
npm install date-fns
typescript
import { format } from 'date-fns';
import { enUS, ptBR, es, fr } from 'date-fns/locale';

const locales = {
  en: enUS,
  pt: ptBR,
  es,
  fr, // ✅ Add this
};

export function useDateFormatter() {
  const { i18n } = useTranslation();
  const locale = locales[i18n.language as keyof typeof locales];

  return {
    formatDate: (date: Date, pattern: string = 'PP') =>
      format(date, pattern, { locale }),
  };
}

Step 5: Update TypeScript Types (Optional)

For type safety, update your i18n types:

src/i18n/types.ts
import en from './locales/en.json';

export type TranslationKeys = typeof en;
export type SupportedLanguage = 'en' | 'pt' | 'es' | 'fr'; // ✅ Add 'fr'

declare module 'react-i18next' {
  interface CustomTypeOptions {
    defaultNS: 'translation';
    resources: {
      translation: TranslationKeys;
    };
  }
}

Validation Checklist

✅ All keys translated

Make sure every key from your base language (en) is present

✅ Pluralization forms

Add _plural variants where needed

✅ Test UI layout

Some languages have longer words - ensure UI doesn't break

✅ RTL support (if needed)

For Arabic/Hebrew, enable RTL layout support

RTL Languages (Arabic, Hebrew)

For right-to-left languages:

typescript
import { I18nManager } from 'react-native';

// In your app initialization
useEffect(() => {
  const rtlLanguages = ['ar', 'he'];
  const isRTL = rtlLanguages.includes(i18n.language);
  
  if (I18nManager.isRTL !== isRTL) {
    I18nManager.forceRTL(isRTL);
    // Restart app for changes to take effect
    // RNRestart.Restart();
  }
}, [i18n.language]);

Testing

Test your new language:

typescript
// In your app
import { useTranslation } from 'react-i18next';

export function TestScreen() {
  const { i18n, t } = useTranslation();

  return (
    <View className="p-4">
      <Text>Current language: {i18n.language}</Text>
      
      <Pressable onPress={() => i18n.changeLanguage('fr')}>
        <Text>Switch to French</Text>
      </Pressable>
      
      <Text>{t('common.welcome')}</Text>
      <Text>{t('auth.login')}</Text>
    </View>
  );
}

Tools & Tips

Translation Tools

  • i18n-ally - VS Code extension for managing translations
  • Crowdin - Collaborative translation platform
  • Lokalise - Translation management system

Best Practices

✓

Keep base language (en) as source of truth

✓

Use professional translators for production apps

✓

Test with native speakers

✓

Consider cultural differences (dates, currency, images)

You can also use tools like Google Translate API to auto-translate strings during development.

Next Steps