327 lines
15 KiB
TypeScript
327 lines
15 KiB
TypeScript
import i18n from "i18next";
|
||
import { initReactI18next } from "react-i18next";
|
||
|
||
// 1. Функция для определения языка из URL
|
||
const getLanguageFromURL = (): string => {
|
||
const path = window.location.pathname;
|
||
const langMatch = path.match(/^\/(en|ru|uz)(\/|$)/i);
|
||
const detectedLang = langMatch ? langMatch[1].toLowerCase() : "ru"; // Фолбэк на 'ru'
|
||
console.log("🔍 Widget i18n: Detected language from URL:", detectedLang, "path:", path);
|
||
return detectedLang;
|
||
};
|
||
|
||
// 2. Локали, встроенные прямо в конфиг
|
||
|
||
const r = {
|
||
ru: {
|
||
translation: {
|
||
"quiz is inactive": "Квиз не активирован",
|
||
"no questions found": "Нет созданных вопросов",
|
||
"quiz is empty": "Квиз пуст",
|
||
"quiz already completed": "Вы уже прошли этот опрос",
|
||
"no quiz id": "Отсутствует id квиза",
|
||
"quiz data is null": "Не были переданы параметры квиза",
|
||
"invalid request data": "Такого квиза не существует",
|
||
"default message": "Что-то пошло не так",
|
||
"The request could not be sent": "Заявка не может быть отправлена",
|
||
"The number of points could not be sent": "Количество баллов не может быть отправлено",
|
||
"Your result": "Ваш результат",
|
||
"Your points": "Ваши баллы",
|
||
of: "из",
|
||
"View answers": "Посмотреть ответы",
|
||
"Find out more": "Узнать подробнее",
|
||
"Go to website": "Перейти на сайт",
|
||
"Question title": "Заголовок вопроса",
|
||
"Question without a title": "Вопрос без названия",
|
||
"Your answer": "Ваш ответ",
|
||
"Add image": "Добавить изображение",
|
||
"Accepts images": "Принимает изображения",
|
||
"Add video": "Добавить видео",
|
||
"Accepts .mp4 and .mov format - maximum 50mb": "Принимает .mp4 и .mov формат — максимум 50мб",
|
||
"Add audio file": "Добавить аудиофайл",
|
||
"Accepts audio files": "Принимает аудиофайлы",
|
||
"Add document": "Добавить документ",
|
||
"Accepts documents": "Принимает документы",
|
||
Next: "Далее",
|
||
Prev: "Назад",
|
||
From: "От",
|
||
До: "До",
|
||
"Enter your answer": "Введите свой ответ",
|
||
"Incorrect file type selected": "Выбран некорректный тип файла",
|
||
"File is too big. Maximum size is 50 MB": "Файл слишком большой. Максимальный размер 50 МБ",
|
||
"Acceptable file extensions": "Допустимые расширения файлов",
|
||
"You have uploaded": "Вы загрузили",
|
||
"The answer was not counted": "Ответ не был засчитан",
|
||
"Select an answer option below": "Выберите вариант ответа ниже",
|
||
"Select an answer option on the left": "Выберите вариант ответа слева",
|
||
"Fill out the form to receive your test results": "Заполните форму, чтобы получить результаты теста",
|
||
Enter: "Введите",
|
||
Name: "Имя",
|
||
"Phone number": "Номер телефона",
|
||
"Last name": "Фамилия",
|
||
Address: "Адрес",
|
||
"Incorrect email entered": "Введена некорректная почта",
|
||
"Please fill in the fields": "Пожалуйста, заполните поля",
|
||
"Please try again later": "повторите попытку позже",
|
||
"Regulation on the processing of personal data": "Положением об обработке персональных данных",
|
||
"Privacy Policy": "Политикой конфиденциальности",
|
||
familiarized: "ознакомлен",
|
||
and: "и",
|
||
"Get results": "Получить результаты",
|
||
"Data sent successfully": "Данные успешно отправлены",
|
||
Step: "Шаг",
|
||
"questions are not ready yet": "Вопросы для аудитории пока не готовы. Подождите",
|
||
"Add your image": "Добавьте своё изображение",
|
||
"select emoji": "выберите смайлик",
|
||
"Please complete the phone number": "Пожалуйста, заполните номер телефона до конца",
|
||
"": "", // Пустой ключ для fallback
|
||
},
|
||
},
|
||
en: {
|
||
translation: {
|
||
"quiz is inactive": "Quiz is inactive",
|
||
"no questions found": "No questions found",
|
||
"quiz is empty": "Quiz is empty",
|
||
"quiz already completed": "You've already completed this quiz",
|
||
"no quiz id": "Missing quiz ID",
|
||
"quiz data is null": "No quiz parameters were provided",
|
||
"invalid request data": "This quiz doesn't exist",
|
||
"default message": "Something went wrong",
|
||
"The request could not be sent": "Request could not be sent",
|
||
"The number of points could not be sent": "Points could not be submitted",
|
||
"Your result": "Your result",
|
||
"Your points": "Your points",
|
||
of: "of",
|
||
"View answers": "View answers",
|
||
"Find out more": "Learn more",
|
||
"Go to website": "Go to website",
|
||
"Question title": "Question title",
|
||
"Question without a title": "Untitled question",
|
||
"Your answer": "Your answer",
|
||
"Add image": "Add image",
|
||
"Accepts images": "Accepts images",
|
||
"Add video": "Add video",
|
||
"Accepts .mp4 and .mov format - maximum 50mb": "Accepts .mp4 and .mov format - maximum 50MB",
|
||
"Add audio file": "Add audio file",
|
||
"Accepts audio files": "Accepts audio files",
|
||
"Add document": "Add document",
|
||
"Accepts documents": "Accepts documents",
|
||
Next: "Next",
|
||
Prev: "Previous",
|
||
From: "From",
|
||
До: "To",
|
||
"Enter your answer": "Enter your answer",
|
||
"Incorrect file type selected": "Invalid file type selected",
|
||
"File is too big. Maximum size is 50 MB": "File is too large. Maximum size is 50 MB",
|
||
"Acceptable file extensions": "Allowed file extensions",
|
||
"You have uploaded": "You've uploaded",
|
||
"The answer was not counted": "Answer wasn't counted",
|
||
"Select an answer option below": "Select an answer option below",
|
||
"Select an answer option on the left": "Select an answer option on the left",
|
||
"Fill out the form to receive your test results": "Fill out the form to receive your test results",
|
||
Enter: "Enter",
|
||
Name: "Name",
|
||
"Phone number": "Phone number",
|
||
"Last name": "Last name",
|
||
Address: "Address",
|
||
"Incorrect email entered": "Invalid email entered",
|
||
"Please fill in the fields": "Please fill in the fields",
|
||
"Please try again later": "Please try again later",
|
||
"Regulation on the processing of personal data": "Personal Data Processing Regulation",
|
||
"Privacy Policy": "Privacy Policy",
|
||
familiarized: "acknowledged",
|
||
and: "and",
|
||
"Get results": "Get results",
|
||
"Data sent successfully": "Data sent successfully",
|
||
Step: "Step",
|
||
"questions are not ready yet": "There are no questions for the audience yet. Please wait",
|
||
"Add your image": "Add your image",
|
||
"select emoji": "select emoji",
|
||
"Please complete the phone number": "Please complete the phone number",
|
||
"": "", // Пустой ключ для fallback
|
||
},
|
||
},
|
||
uz: {
|
||
translation: {
|
||
"quiz is inactive": "Test faol emas",
|
||
"no questions found": "Savollar topilmadi",
|
||
"quiz is empty": "Test boʻsh",
|
||
"quiz already completed": "Siz bu testni allaqachon topshirgansiz",
|
||
"no quiz id": "Test IDsi yoʻq",
|
||
"quiz data is null": "Test parametrlari yuborilmagan",
|
||
"invalid request data": "Bunday test mavjud emas",
|
||
"default message": "Xatolik yuz berdi",
|
||
"The request could not be sent": "Soʻrov yuborib boʻlmadi",
|
||
"The number of points could not be sent": "Ballar yuborib boʻlmadi",
|
||
"Your result": "Sizning natijangiz",
|
||
"Your points": "Sizning ballaringiz",
|
||
of: "/",
|
||
"View answers": "Javoblarni koʻrish",
|
||
"Find out more": "Batafsil maʼlumot",
|
||
"Go to website": "Veb-saytga oʻtish",
|
||
"Question title": "Savol sarlavhasi",
|
||
"Question without a title": "Sarlavhasiz savol",
|
||
"Your answer": "Sizning javobingiz",
|
||
"Add image": "Rasm qoʻshish",
|
||
"Accepts images": "Rasmlarni qabul qiladi",
|
||
"Add video": "Video qoʻshish",
|
||
"Accepts .mp4 and .mov format - maximum 50mb": ".mp4 va .mov formatlarini qabul qiladi - maksimal 50MB",
|
||
"Add audio file": "Audio fayl qoʻshish",
|
||
"Accepts audio files": "Audio fayllarni qabul qiladi",
|
||
"Add document": "Hujjat qoʻshish",
|
||
"Accepts documents": "Hujjatlarni qabul qiladi",
|
||
Next: "Keyingi",
|
||
Prev: "Oldingi",
|
||
From: "Dan",
|
||
До: "Gacha",
|
||
"Enter your answer": "Javobingizni kiriting",
|
||
"Incorrect file type selected": "Notoʻgʻri fayl turi tanlandi",
|
||
"File is too big. Maximum size is 50 MB": "Fayl juda katta. Maksimal hajmi 50 MB",
|
||
"Acceptable file extensions": "Qabul qilinadigan fayl kengaytmalari",
|
||
"You have uploaded": "Siz yuklagansiz",
|
||
"The answer was not counted": "Javob hisobga olinmadi",
|
||
"Select an answer option below": "Quyidagi javob variantlaridan birini tanlang",
|
||
"Select an answer option on the left": "Chapdagi javob variantlaridan birini tanlang",
|
||
"Fill out the form to receive your test results": "Test natijalaringizni olish uchun shaklni toʻldiring",
|
||
Enter: "Kiriting",
|
||
Name: "Ism",
|
||
"Phone number": "Telefon raqami",
|
||
"Last name": "Familiya",
|
||
Address: "Manzil",
|
||
"Incorrect email entered": "Notoʻgʻri elektron pochta kiritildi",
|
||
"Please fill in the fields": "Iltimos, maydonlarni toʻldiring",
|
||
"Please try again later": "Iltimos, keyinroq urinib koʻring",
|
||
"Regulation on the processing of personal data": "Shaxsiy maʼlumotlarni qayta ishlash qoidalari",
|
||
"Privacy Policy": "Maxfiylik siyosati",
|
||
familiarized: "tanishdim",
|
||
and: "va",
|
||
"Get results": "Natijalarni olish",
|
||
"Data sent successfully": "Ma'lumotlar muvaffaqiyatli yuborildi",
|
||
Step: "Qadam",
|
||
"questions are not ready yet": "Tomoshabinlar uchun hozircha savollar yo'q. Iltimos kuting",
|
||
"Add your image": "Rasmingizni qo'shing",
|
||
"select emoji": "emoji tanlang",
|
||
"Please complete the phone number": "Iltimos, telefon raqamini to'liq kiriting",
|
||
"": "", // Пустой ключ для fallback
|
||
},
|
||
},
|
||
};
|
||
|
||
console.log("🚀 Widget i18n: Starting initialization...");
|
||
console.log("🔍 Widget i18n: i18next instance before init:", i18n);
|
||
console.log("🔍 Widget i18n: i18next isInitialized before init:", i18n.isInitialized);
|
||
|
||
// Проверяем, не инициализирован ли уже i18n
|
||
if (i18n.isInitialized) {
|
||
console.log("⚠️ Widget i18n: i18n already initialized, adding resources to existing instance");
|
||
|
||
// Добавляем ресурсы к существующему экземпляру
|
||
(Object.keys(r) as Array<"ru" | "en" | "uz">).forEach((lng) => {
|
||
if (i18n.store.data[lng] && i18n.store.data[lng].translation) {
|
||
// Объединяем с существующими переводами
|
||
i18n.store.data[lng].translation = {
|
||
...(i18n.store.data[lng].translation as Record<string, string>),
|
||
...r[lng].translation,
|
||
};
|
||
} else {
|
||
// Добавляем новые переводы
|
||
i18n.store.data[lng] = {
|
||
...(i18n.store.data[lng] as Record<string, any>),
|
||
translation: r[lng].translation,
|
||
};
|
||
}
|
||
});
|
||
|
||
console.log("✅ Widget i18n: Resources added to existing i18n instance");
|
||
console.log("🔍 Widget i18n: Testing translations after adding resources:");
|
||
console.log("🔍 Widget i18n: t('Step'):", i18n.t("Step"));
|
||
console.log("🔍 Widget i18n: t('of'):", i18n.t("of"));
|
||
console.log("🔍 Widget i18n: t('Prev'):", i18n.t("Prev"));
|
||
console.log("🔍 Widget i18n: t('Next'):", i18n.t("Next"));
|
||
} else {
|
||
// 3. Конфигурация i18n без Backend
|
||
i18n
|
||
.use(initReactI18next)
|
||
.init({
|
||
resources: r, // Используем встроенные переводы
|
||
lng: getLanguageFromURL(),
|
||
fallbackLng: "ru",
|
||
supportedLngs: ["en", "ru", "uz"],
|
||
debug: true,
|
||
interpolation: {
|
||
escapeValue: false,
|
||
},
|
||
react: {
|
||
useSuspense: false,
|
||
},
|
||
detection: {
|
||
order: ["path"],
|
||
lookupFromPathIndex: 0,
|
||
caches: [],
|
||
},
|
||
parseMissingKeyHandler: (key) => {
|
||
console.warn("⚠️ Widget i18n: Missing translation key:", key);
|
||
return key;
|
||
},
|
||
missingKeyHandler: (lngs, ns, key) => {
|
||
console.error("🚨 Widget i18n: Missing i18n key:", {
|
||
key,
|
||
languages: lngs,
|
||
namespace: ns,
|
||
stack: new Error().stack,
|
||
});
|
||
},
|
||
})
|
||
.then(() => {
|
||
console.log("✅ Widget i18n: Initialization completed successfully");
|
||
console.log("🔍 Widget i18n: Current language:", i18n.language);
|
||
console.log("🔍 Widget i18n: Available languages:", i18n.languages);
|
||
console.log("🔍 Widget i18n: Available keys for ru:", Object.keys(r.ru.translation));
|
||
console.log("🔍 Widget i18n: Available keys for en:", Object.keys(r.en.translation));
|
||
console.log("🔍 Widget i18n: Available keys for uz:", Object.keys(r.uz.translation));
|
||
console.log("🔍 Widget i18n: Looking for keys: Step, of, Prev, Next");
|
||
console.log("🔍 Widget i18n: Step in ru:", r.ru.translation.Step);
|
||
console.log("🔍 Widget i18n: of in ru:", r.ru.translation.of);
|
||
console.log("🔍 Widget i18n: Prev in ru:", r.ru.translation.Prev);
|
||
console.log("🔍 Widget i18n: Next in ru:", r.ru.translation.Next);
|
||
|
||
// Проверяем, что ключи доступны через i18n
|
||
console.log("🔍 Widget i18n: Testing translations through i18n:");
|
||
console.log("🔍 Widget i18n: t('Step'):", i18n.t("Step"));
|
||
console.log("🔍 Widget i18n: t('of'):", i18n.t("of"));
|
||
console.log("🔍 Widget i18n: t('Prev'):", i18n.t("Prev"));
|
||
console.log("🔍 Widget i18n: t('Next'):", i18n.t("Next"));
|
||
|
||
// Проверяем состояние i18n
|
||
console.log("🔍 Widget i18n: i18n.isInitialized:", i18n.isInitialized);
|
||
console.log("🔍 Widget i18n: i18n.store.data:", i18n.store.data);
|
||
console.log("🔍 Widget i18n: i18n.store.options:", i18n.store.options);
|
||
})
|
||
.catch((error) => {
|
||
console.error("❌ Widget i18n: Initialization failed:", error);
|
||
});
|
||
}
|
||
|
||
// 4. Логирование событий
|
||
i18n.on("languageChanged", (lng) => {
|
||
console.log("🔄 Widget i18n: Language changed to:", lng);
|
||
});
|
||
|
||
i18n.on("initialized", (options) => {
|
||
console.log("🎯 Widget i18n: Initialized event fired with options:", options);
|
||
});
|
||
|
||
i18n.on("loaded", (loaded) => {
|
||
console.log("📦 Widget i18n: Loaded event fired:", loaded);
|
||
});
|
||
|
||
i18n.on("failedLoading", (lng, ns, msg) => {
|
||
console.error("💥 Widget i18n: Failed loading:", { lng, ns, msg });
|
||
});
|
||
|
||
i18n.on("missingKey", (lngs, namespace, key, res) => {
|
||
console.warn("⚠️ Widget i18n: Missing key event:", { lngs, namespace, key, res });
|
||
});
|
||
|
||
export default i18n;
|