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