Created
December 29, 2025 19:54
-
-
Save sunmeat/42a229f47fcd5f620fb783e789488bac to your computer and use it in GitHub Desktop.
api.mymemory.translated.net in web api client app example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const API_BASE_URL = 'https://localhost:7110/api/students'; | |
| // !!! кеш для перекладів: ключ = "текст у нижньому регістрі | цільова мова" | |
| const translationCache = new Map(); | |
| // !!! функція перекладу тексту з української на потрібну мову через mymemory api (це безкоштовно, з підтримкою cors, без апі-ключів) | |
| async function translateText(text, targetLng) { | |
| if (!text || typeof text !== 'string') return text; | |
| text = text.trim(); | |
| if (targetLng === 'uk' || !text) return text; | |
| const cacheKey = `${text.toLowerCase()}|${targetLng}`; | |
| if (translationCache.has(cacheKey)) { | |
| return translationCache.get(cacheKey); | |
| } | |
| const target = targetLng === 'en' ? 'en' : 'fr'; | |
| try { | |
| const url = `https://api.mymemory.translated.net/get?q=${encodeURIComponent(text)}&langpair=uk|${target}`; | |
| const response = await fetch(url); | |
| if (!response.ok) throw new Error(`HTTP ${response.status}`); | |
| const data = await response.json(); | |
| // найкращий метч або перший результат | |
| const translated = data.matches?.[0]?.translation || data.responseData?.translatedText || text; | |
| translationCache.set(cacheKey, translated); | |
| return translated; | |
| } catch (err) { | |
| console.warn(`переклад не вдався для "${text}" → ${targetLng}:`, err); | |
| return text; // fallback на оригінал | |
| } | |
| } | |
| // !!! створення рядка таблиці з перекладеними ім'ям та прізвищем | |
| async function createRow(student, currentLng = 'uk') { | |
| const name = currentLng === 'uk' ? student.name : await translateText(student.name, currentLng); | |
| const surname = currentLng === 'uk' ? student.surname : await translateText(student.surname, currentLng); | |
| return ` | |
| <tr data-rowid="${student.id}" class="student-row"> | |
| <td class="student-cell student-id-cell">${student.id}</td> | |
| <td class="student-cell student-name-cell">${name}</td> | |
| <td class="student-cell student-surname-cell">${surname}</td> | |
| <td class="student-cell student-age-cell">${student.age}</td> | |
| <td class="student-cell student-gpa-cell">${student.gpa}</td> | |
| <td class="student-cell student-actions-cell"> | |
| <a href="#" class="edit-link action-link" data-id="${student.id}">${i18next.t('table.edit')}</a> | | |
| <a href="#" class="remove-link action-link" data-id="${student.id}">${i18next.t('table.delete')}</a> | |
| </td> | |
| </tr> | |
| `; | |
| } | |
| document.addEventListener('DOMContentLoaded', () => { | |
| i18next | |
| .use(i18nextHttpBackend) | |
| .use(i18nextBrowserLanguageDetector) | |
| .init({ | |
| lng: 'uk', | |
| fallbackLng: 'uk', | |
| supportedLngs: ['uk', 'en', 'fr'], | |
| backend: { | |
| loadPath: '/locales/{{lng}}/translation.json' | |
| }, | |
| detection: { | |
| order: ['querystring', 'cookie', 'localStorage', 'navigator', 'htmlTag'], | |
| lookupQuerystring: 'lang', | |
| lookupCookie: 'i18nextLng', | |
| lookupLocalStorage: 'i18nextLng', | |
| caches: ['localStorage', 'cookie'] | |
| }, | |
| debug: false | |
| }, async (err, t) => { | |
| if (err) return console.error(err); | |
| updateContent(); | |
| await getStudents(); | |
| initEventListeners(); | |
| document.getElementById('languageSwitcher').addEventListener('change', (e) => { | |
| const newLng = e.target.value; | |
| i18next.changeLanguage(newLng, async () => { | |
| updateContent(); | |
| // !!! очищаємо кеш перекладів при зміні мови | |
| translationCache.clear(); | |
| await getStudents(); | |
| }); | |
| }); | |
| }); | |
| }); | |
| function updateContent() { | |
| document.documentElement.lang = i18next.language; | |
| document.querySelector('title').textContent = i18next.t('title'); | |
| document.querySelector('.page-title').textContent = i18next.t('title'); | |
| document.querySelector('label[for="name"]').textContent = i18next.t('form.name'); | |
| document.querySelector('label[for="surname"]').textContent = i18next.t('form.surname'); | |
| document.querySelector('label[for="age"]').textContent = i18next.t('form.age'); | |
| document.querySelector('label[for="gpa"]').textContent = i18next.t('form.gpa'); | |
| document.querySelector('.btn-submit').textContent = i18next.t('form.save'); | |
| document.querySelector('.btn-reset').textContent = i18next.t('form.reset'); | |
| document.querySelector('.table-header th:nth-child(1)').textContent = i18next.t('table.id'); | |
| document.querySelector('.table-header th:nth-child(2)').textContent = i18next.t('table.name'); | |
| document.querySelector('.table-header th:nth-child(3)').textContent = i18next.t('table.surname'); | |
| document.querySelector('.table-header th:nth-child(4)').textContent = i18next.t('table.age'); | |
| document.querySelector('.table-header th:nth-child(5)').textContent = i18next.t('table.gpa'); | |
| document.querySelector('.table-header th:nth-child(6)').textContent = i18next.t('table.actions'); | |
| } | |
| // !!! отримання списку студентів з перекладом даних з БД | |
| async function getStudents() { | |
| try { | |
| const response = await fetch(API_BASE_URL, { | |
| method: 'GET', | |
| headers: { 'Content-Type': 'application/json' } | |
| }); | |
| if (!response.ok) { | |
| throw new Error(i18next.t('errors.httpError', { status: response.status })); | |
| } | |
| const students = await response.json(); | |
| const tbody = document.querySelector('.students-tbody'); | |
| tbody.innerHTML = ''; | |
| const currentLng = i18next.language; | |
| for (const student of students) { | |
| const rowHtml = await createRow(student, currentLng); | |
| tbody.insertAdjacentHTML('beforeend', rowHtml); | |
| } | |
| } catch (error) { | |
| alert(i18next.t('errors.load', { message: error.message })); | |
| } | |
| } | |
| async function getStudent(id) { | |
| try { | |
| const response = await fetch(`${API_BASE_URL}/${id}`, { | |
| method: 'GET', | |
| headers: { 'Content-Type': 'application/json' } | |
| }); | |
| if (!response.ok) { | |
| throw new Error(i18next.t('errors.httpError', { status: response.status })); | |
| } | |
| const student = await response.json(); | |
| const form = document.forms['studentForm']; | |
| form.elements['Id'].value = student.id; | |
| form.elements['name'].value = student.name; | |
| form.elements['surname'].value = student.surname; | |
| form.elements['age'].value = student.age; | |
| form.elements['gpa'].value = student.gpa; | |
| } catch (error) { | |
| alert(i18next.t('errors.fetchOne', { message: error.message })); | |
| } | |
| } | |
| // !!! створення нового студента з подальшим перекладом у таблиці | |
| async function createStudent(studentData) { | |
| try { | |
| const response = await fetch(API_BASE_URL, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(studentData) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(i18next.t('errors.httpError', { status: response.status })); | |
| } | |
| const student = await response.json(); | |
| const rowHtml = await createRow(student, i18next.language); | |
| document.querySelector('.students-tbody').insertAdjacentHTML('beforeend', rowHtml); | |
| resetForm(); | |
| } catch (error) { | |
| alert(i18next.t('errors.create', { message: error.message })); | |
| } | |
| } | |
| // !!! редагування студента з оновленням перекладеного рядка | |
| async function editStudent(studentData) { | |
| try { | |
| const response = await fetch(API_BASE_URL, { | |
| method: 'PUT', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(studentData) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(i18next.t('errors.httpError', { status: response.status })); | |
| } | |
| const student = await response.json(); | |
| const row = document.querySelector(`tr[data-rowid='${student.id}']`); | |
| if (row) { | |
| const newRowHtml = await createRow(student, i18next.language); | |
| row.outerHTML = newRowHtml; | |
| } | |
| resetForm(); | |
| } catch (error) { | |
| alert(i18next.t('errors.update', { message: error.message })); | |
| } | |
| } | |
| async function deleteStudent(id) { | |
| if (!confirm(i18next.t('confirmDelete'))) { | |
| return; | |
| } | |
| try { | |
| const response = await fetch(`${API_BASE_URL}/${id}`, { | |
| method: 'DELETE', | |
| headers: { 'Content-Type': 'application/json' } | |
| }); | |
| if (!response.ok) { | |
| throw new Error(i18next.t('errors.httpError', { status: response.status })); | |
| } | |
| const row = document.querySelector(`tr[data-rowid='${id}']`); | |
| if (row) { | |
| row.remove(); | |
| } | |
| } catch (error) { | |
| alert(i18next.t('errors.delete', { message: error.message })); | |
| } | |
| } | |
| function initEventListeners() { | |
| const form = document.forms['studentForm']; | |
| const submitBtn = document.querySelector('.btn-submit'); | |
| const resetBtn = document.querySelector('.btn-reset'); | |
| const tbody = document.querySelector('.students-tbody'); | |
| submitBtn.addEventListener('click', handleSubmit); | |
| resetBtn.addEventListener('click', handleReset); | |
| tbody.addEventListener('click', (e) => { | |
| if (e.target.classList.contains('edit-link')) { | |
| const id = e.target.dataset.id; | |
| getStudent(id); | |
| } else if (e.target.classList.contains('remove-link')) { | |
| const id = e.target.dataset.id; | |
| deleteStudent(id); | |
| } | |
| }); | |
| } | |
| function resetForm() { | |
| const form = document.forms['studentForm']; | |
| form.reset(); | |
| form.elements['Id'].value = 0; | |
| } | |
| function handleReset(e) { | |
| e.preventDefault(); | |
| resetForm(); | |
| } | |
| function handleSubmit(e) { | |
| e.preventDefault(); | |
| const form = document.forms['studentForm']; | |
| const id = parseInt(form.elements['Id'].value); | |
| const name = form.elements['name'].value.trim(); | |
| const surname = form.elements['surname'].value.trim(); | |
| const age = parseInt(form.elements['age'].value); | |
| const gpa = parseFloat(form.elements['gpa'].value); | |
| const studentData = { name, surname, age, gpa }; | |
| if (id === 0) { | |
| createStudent(studentData); | |
| } else { | |
| studentData.id = id; | |
| editStudent(studentData); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment