Перейти к основному содержимому

i18n

Локализация на 3 языка: English, Русский, Українська.

Структура

src/core/i18n/
├── config.ts # Конфигурация i18next
├── locales/
│ ├── en.json # English
│ ├── ru.json # Русский
│ └── uk.json # Українська
└── index.ts # Экспорты

Конфигурация

// src/core/i18n/config.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import * as Localization from 'expo-localization';
import AsyncStorage from '@react-native-async-storage/async-storage';

import en from './locales/en.json';
import ru from './locales/ru.json';
import uk from './locales/uk.json';

const LANGUAGE_KEY = 'user-language';

export async function initI18n() {
// Попробовать загрузить сохранённый язык
const savedLanguage = await AsyncStorage.getItem(LANGUAGE_KEY);

// Или определить по устройству
const deviceLanguage = Localization.locale.split('-')[0];
const defaultLanguage = ['en', 'ru', 'uk'].includes(deviceLanguage)
? deviceLanguage
: 'en';

await i18n
.use(initReactI18next)
.init({
resources: {
en: { translation: en },
ru: { translation: ru },
uk: { translation: uk },
},
lng: savedLanguage || defaultLanguage,
fallbackLng: 'en',
interpolation: {
escapeValue: false,
},
});

return i18n;
}

export async function changeLanguage(language: string) {
await i18n.changeLanguage(language);
await AsyncStorage.setItem(LANGUAGE_KEY, language);
}

export function getCurrentLanguage() {
return i18n.language;
}

Файлы локализации

// locales/en.json
{
"common": {
"loading": "Loading...",
"error": "Something went wrong",
"retry": "Try again",
"cancel": "Cancel",
"save": "Save",
"delete": "Delete"
},
"auth": {
"login": "Sign In",
"register": "Sign Up",
"logout": "Sign Out",
"email": "Email",
"password": "Password",
"forgotPassword": "Forgot password?"
},
"courses": {
"title": "My Courses",
"empty": "No courses yet",
"create": "Create Course",
"modules": "{{count}} modules",
"progress": "{{percent}}% complete"
},
"lessons": {
"content": "Content",
"videos": "Videos",
"resources": "Resources",
"complete": "Mark as Complete"
},
"games": {
"title": "Brain Games",
"memoryCards": "Memory Cards",
"patternSequence": "Pattern Sequence",
"learningStyle": "Learning Style Quiz"
},
"profile": {
"title": "Profile",
"xp": "XP",
"streak": "Day Streak",
"level": "Level"
},
"settings": {
"title": "Settings",
"theme": "Theme",
"language": "Language",
"notifications": "Notifications",
"subscription": "Subscription"
}
}
// locales/ru.json
{
"common": {
"loading": "Загрузка...",
"error": "Что-то пошло не так",
"retry": "Попробовать снова",
"cancel": "Отмена",
"save": "Сохранить",
"delete": "Удалить"
},
"auth": {
"login": "Войти",
"register": "Регистрация",
"logout": "Выйти",
"email": "Email",
"password": "Пароль",
"forgotPassword": "Забыли пароль?"
},
"courses": {
"title": "Мои курсы",
"empty": "Курсов пока нет",
"create": "Создать курс",
"modules": "{{count}} модулей",
"progress": "{{percent}}% пройдено"
}
// ... остальные ключи
}

Использование

useTranslation Hook

import { useTranslation } from 'react-i18next';

function CoursesScreen() {
const { t } = useTranslation();

return (
<View>
<Text>{t('courses.title')}</Text>
<Text>{t('courses.modules', { count: 5 })}</Text>
<Text>{t('courses.progress', { percent: 75 })}</Text>
</View>
);
}

Trans компонент (для сложного форматирования)

import { Trans } from 'react-i18next';

function WelcomeMessage({ name }) {
return (
<Trans
i18nKey="welcome.message"
values={{ name }}
components={{
bold: <Text className="font-bold" />,
}}
/>
);
}
{
"welcome": {
"message": "Привет, <bold>{{name}}</bold>!"
}
}

Переключение языка

// settings/language.tsx
import { useTranslation } from 'react-i18next';
import { changeLanguage } from '@/core/i18n';

function LanguageSettings() {
const { i18n } = useTranslation();
const currentLanguage = i18n.language;

const languages = [
{ code: 'en', label: 'English' },
{ code: 'ru', label: 'Русский' },
{ code: 'uk', label: 'Українська' },
];

return (
<View>
{languages.map(lang => (
<RadioButton
key={lang.code}
selected={currentLanguage === lang.code}
onPress={() => changeLanguage(lang.code)}
label={lang.label}
/>
))}
</View>
);
}

Инициализация

// app/_layout.tsx
import { initI18n } from '@/core/i18n';

export default function RootLayout() {
const [i18nReady, setI18nReady] = useState(false);

useEffect(() => {
initI18n().then(() => setI18nReady(true));
}, []);

if (!i18nReady) return <SplashScreen />;

return <App />;
}

Plural Forms

{
"lessons": {
"count_one": "{{count}} урок",
"count_few": "{{count}} урока",
"count_many": "{{count}} уроков"
}
}
// Автоматически выберет правильную форму
t('lessons.count', { count: 1 }) // "1 урок"
t('lessons.count', { count: 3 }) // "3 урока"
t('lessons.count', { count: 5 }) // "5 уроков"