feat(i18n): bring the Azerbaijani translation up to date with dev

Translates the keys dev gained since the branch was cut, the help center
above all, and drops the six keys dev no longer has. Registers az
everywhere a language is listed: notification emails, the rate-limit
message, the placeholder check and the wiki. The two tests that counted
the languages by hand now derive the count from SUPPORTED_LANGUAGES.
This commit is contained in:
Maurice
2026-09-24 16:45:02 +02:00
parent 9b843bee80
commit 33aa1cd52e
14 changed files with 4000 additions and 135 deletions
+1
View File
@@ -124,6 +124,7 @@ const RATE_LIMIT_MESSAGES: Record<string, string> = {
it: 'Troppi tentativi. Riprova più tardi.', it: 'Troppi tentativi. Riprova più tardi.',
tr: 'Çok fazla deneme. Lütfen daha sonra tekrar deneyin.', tr: 'Çok fazla deneme. Lütfen daha sonra tekrar deneyin.',
ar: 'محاولات كثيرة جدًا. يرجى المحاولة لاحقًا.', ar: 'محاولات كثيرة جدًا. يرجى المحاولة لاحقًا.',
az: 'Həddindən çox cəhd edildi. Bir az sonra yenidən cəhd edin.',
id: 'Terlalu banyak percobaan. Coba lagi nanti.', id: 'Terlalu banyak percobaan. Coba lagi nanti.',
ja: '試行回数が多すぎます。時間をおいて再度お試しください。', ja: '試行回数が多すぎます。時間をおいて再度お試しください。',
ko: '시도 횟수가 너무 많습니다. 잠시 후 다시 시도해 주세요.', ko: '시도 횟수가 너무 많습니다. 잠시 후 다시 시도해 주세요.',
+2 -1
View File
@@ -9,6 +9,7 @@ import { describe, it, expect, beforeEach, vi } from 'vitest';
import { render, screen, fireEvent, cleanup } from '../../tests/helpers/render'; import { render, screen, fireEvent, cleanup } from '../../tests/helpers/render';
import { resetAllStores, seedStore } from '../../tests/helpers/store'; import { resetAllStores, seedStore } from '../../tests/helpers/store';
import { useSettingsStore } from '../store/settingsStore'; import { useSettingsStore } from '../store/settingsStore';
import { SUPPORTED_LANGUAGES } from '../i18n';
import LoginPage from './LoginPage'; import LoginPage from './LoginPage';
import { useLogin } from './login/useLogin'; import { useLogin } from './login/useLogin';
@@ -153,7 +154,7 @@ describe('LoginPage — language switcher', () => {
expect(screen.getByRole('button', { name: 'Change language' })).toHaveAttribute('aria-expanded', 'true'); expect(screen.getByRole('button', { name: 'Change language' })).toHaveAttribute('aria-expanded', 'true');
const options = screen.getAllByRole('option'); const options = screen.getAllByRole('option');
expect(options).toHaveLength(23); expect(options).toHaveLength(SUPPORTED_LANGUAGES.length);
expect(screen.getByRole('option', { name: 'Deutsch' })).toHaveAttribute('aria-selected', 'false'); expect(screen.getByRole('option', { name: 'Deutsch' })).toHaveAttribute('aria-selected', 'false');
expect(screen.getByRole('option', { name: 'English' })).toHaveAttribute('aria-selected', 'true'); expect(screen.getByRole('option', { name: 'English' })).toHaveAttribute('aria-selected', 'true');
}); });
+1 -1
View File
@@ -91,7 +91,7 @@ describe('isRtlLanguage', () => {
describe('SUPPORTED_LANGUAGES', () => { describe('SUPPORTED_LANGUAGES', () => {
it('FE-COMP-I18N-009: contains expected entries with value/label shape', () => { it('FE-COMP-I18N-009: contains expected entries with value/label shape', () => {
expect(Array.isArray(SUPPORTED_LANGUAGES)).toBe(true) expect(Array.isArray(SUPPORTED_LANGUAGES)).toBe(true)
expect(SUPPORTED_LANGUAGES).toHaveLength(23) expect(new Set(SUPPORTED_LANGUAGES.map(l => l.value)).size).toBe(SUPPORTED_LANGUAGES.length)
expect(SUPPORTED_LANGUAGES).toContainEqual(expect.objectContaining({ value: 'en', label: 'English' })) expect(SUPPORTED_LANGUAGES).toContainEqual(expect.objectContaining({ value: 'en', label: 'English' }))
expect(SUPPORTED_LANGUAGES).toContainEqual(expect.objectContaining({ value: 'tr', label: 'Türkçe' })) expect(SUPPORTED_LANGUAGES).toContainEqual(expect.objectContaining({ value: 'tr', label: 'Türkçe' }))
expect(SUPPORTED_LANGUAGES).toContainEqual(expect.objectContaining({ value: 'ja', label: '日本語' })) expect(SUPPORTED_LANGUAGES).toContainEqual(expect.objectContaining({ value: 'ja', label: '日本語' }))
+1
View File
@@ -173,6 +173,7 @@ const admin: TranslationStrings = {
'admin.amapKey': 'Amap (高德地图) API açarı', 'admin.amapKey': 'Amap (高德地图) API açarı',
'admin.amapKeyHint': 'admin.amapKeyHint':
'Google-un əlçatmaz, OpenStreetMap əhatəsinin isə zəif olduğu materik Çinində məkan axtarışı üçün. JS API açarı deyil, "Web 服务" (veb xidməti) açarı tələb olunur. Onu console.amap.com saytından əldə edin.', 'Google-un əlçatmaz, OpenStreetMap əhatəsinin isə zəif olduğu materik Çinində məkan axtarışı üçün. JS API açarı deyil, "Web 服务" (veb xidməti) açarı tələb olunur. Onu console.amap.com saytından əldə edin.',
'admin.keyFromEnv': '{name} ilə təyin edilib',
'admin.placesProvider.title': 'Məkan axtarışı provayderi', 'admin.placesProvider.title': 'Məkan axtarışı provayderi',
'admin.placesProvider.subtitle': 'admin.placesProvider.subtitle':
'TREK-in öz indeksi və OpenStreetMap hər axtarışa cavab verir. Onlar heç nə tapmadıqda başqa kimə sorğu göndəriləcəyini bu seçim müəyyən edir: Avtomatik rejim açar mövcud olduqda əvvəlcə Google-a, sonra Amap-a üstünlük verir.', 'TREK-in öz indeksi və OpenStreetMap hər axtarışa cavab verir. Onlar heç nə tapmadıqda başqa kimə sorğu göndəriləcəyini bu seçim müəyyən edir: Avtomatik rejim açar mövcud olduqda əvvəlcə Google-a, sonra Amap-a üstünlük verir.',
+1 -1
View File
@@ -114,7 +114,7 @@ const dayplan: TranslationStrings = {
'dayplan.reorderError': 'dayplan.reorderError':
'Günlərin sırasını dəyişmək mümkün olmadı', 'Günlərin sırasını dəyişmək mümkün olmadı',
'dayplan.addDayError': 'Gün əlavə etmək mümkün olmadı', 'dayplan.addDayError': 'Gün əlavə etmək mümkün olmadı',
'dayplan.deleteDay': 'Günü sil', 'dayplan.deleteDay': 'Günü sil',
'dayplan.deleteDayTitle': '{day} silinsin?', 'dayplan.deleteDayTitle': '{day} silinsin?',
'dayplan.deleteDayBody': 'Gün səyahətdən silinəcək. Bunu geri qaytarmaq mümkün deyil.', 'dayplan.deleteDayBody': 'Gün səyahətdən silinəcək. Bunu geri qaytarmaq mümkün deyil.',
'dayplan.deleteDayEmpty': 'Bu gün üçün heç nə planlaşdırılmayıb.', 'dayplan.deleteDayEmpty': 'Bu gün üçün heç nə planlaşdırılmayıb.',
File diff suppressed because it is too large Load Diff
+2
View File
@@ -28,6 +28,8 @@ const login: TranslationStrings = {
'login.oidcSignIn': '{name} ilə daxil ol', 'login.oidcSignIn': '{name} ilə daxil ol',
'login.oidcOnly': 'Parolla autentifikasiya deaktiv edilib. SSO provayderinizdən istifadə edərək daxil olun.', 'login.oidcOnly': 'Parolla autentifikasiya deaktiv edilib. SSO provayderinizdən istifadə edərək daxil olun.',
'login.oidcLoggedOut': 'Sistemdən çıxmısınız. SSO provayderinizdən istifadə edərək yenidən daxil olun.', 'login.oidcLoggedOut': 'Sistemdən çıxmısınız. SSO provayderinizdən istifadə edərək yenidən daxil olun.',
'login.oidcRedirecting': 'Sizi {name} səhifəsinə yönləndiririk…',
'login.oidcRedirectSlow': 'Hələ buradasınız? {name} ilə davam edin',
'login.demoHint': 'Demonu sınayın — qeydiyyat tələb olunmur', 'login.demoHint': 'Demonu sınayın — qeydiyyat tələb olunmur',
'login.mfaTitle': 'İki faktorlu autentifikasiya', 'login.mfaTitle': 'İki faktorlu autentifikasiya',
'login.mfaSubtitle': 'Autentifikator tətbiqinizdəki 6 rəqəmli kodu daxil edin.', 'login.mfaSubtitle': 'Autentifikator tətbiqinizdəki 6 rəqəmli kodu daxil edin.',
-127
View File
@@ -31,9 +31,6 @@ const packing: TranslationStrings = {
'packing.clearChecked': 'İşarələnmiş {count} elementi sil', 'packing.clearChecked': 'İşarələnmiş {count} elementi sil',
'packing.clearCheckedShort': '{count} elementi sil', 'packing.clearCheckedShort': '{count} elementi sil',
'packing.suggestions': 'Təkliflər',
'packing.suggestionsTitle': 'Təklifləri əlavə et',
'packing.allSuggested': 'Bütün təkliflər əlavə edilib',
'packing.allPacked': 'Hər şey yığılıb!', 'packing.allPacked': 'Hər şey yığılıb!',
'packing.addPlaceholder': 'Yeni element əlavə et...', 'packing.addPlaceholder': 'Yeni element əlavə et...',
@@ -43,8 +40,6 @@ const packing: TranslationStrings = {
'packing.filterDone': 'Hazırdır', 'packing.filterDone': 'Hazırdır',
'packing.emptyTitle': 'Baqaj siyahısı boşdur', 'packing.emptyTitle': 'Baqaj siyahısı boşdur',
'packing.emptyHint':
'Elementlər əlavə edin və ya təkliflərdən istifadə edin',
'packing.emptyFiltered': 'packing.emptyFiltered':
'Bu filtrə uyğun heç bir element yoxdur', 'Bu filtrə uyğun heç bir element yoxdur',
@@ -110,128 +105,6 @@ const packing: TranslationStrings = {
'packing.toast.renameError': 'Adını dəyişmək mümkün olmadı', 'packing.toast.renameError': 'Adını dəyişmək mümkün olmadı',
'packing.toast.addError': 'Əlavə etmək mümkün olmadı', 'packing.toast.addError': 'Əlavə etmək mümkün olmadı',
'packing.suggestions.items': [
{
name: 'Pasport',
category: 'Sənədlər',
},
{
name: 'Şəxsiyyət vəsiqəsi',
category: 'Sənədlər',
},
{
name: 'Səyahət sığortası',
category: 'Sənədlər',
},
{
name: 'Uçuş biletləri',
category: 'Sənədlər',
},
{
name: 'Kredit kartı',
category: 'Maliyyə',
},
{
name: 'Nağd pul',
category: 'Maliyyə',
},
{
name: 'Viza',
category: 'Sənədlər',
},
{
name: 'Köynəklər',
category: 'Geyim',
},
{
name: 'Şalvarlar',
category: 'Geyim',
},
{
name: 'Alt paltarı',
category: 'Geyim',
},
{
name: 'Corablar',
category: 'Geyim',
},
{
name: 'Gödəkçə',
category: 'Geyim',
},
{
name: 'Yuxu geyimi',
category: 'Geyim',
},
{
name: 'Çimərlik geyimi',
category: 'Geyim',
},
{
name: 'Yağış gödəkçəsi',
category: 'Geyim',
},
{
name: 'Rahat ayaqqabı',
category: 'Geyim',
},
{
name: 'Diş fırçası',
category: 'Gigiyena vasitələri',
},
{
name: 'Diş məcunu',
category: 'Gigiyena vasitələri',
},
{
name: 'Şampun',
category: 'Gigiyena vasitələri',
},
{
name: 'Dezodorant',
category: 'Gigiyena vasitələri',
},
{
name: 'Günəş kremi',
category: 'Gigiyena vasitələri',
},
{
name: 'Ülgüc',
category: 'Gigiyena vasitələri',
},
{
name: 'Şarj cihazı',
category: 'Elektronika',
},
{
name: 'Powerbank',
category: 'Elektronika',
},
{
name: 'Qulaqlıq',
category: 'Elektronika',
},
{
name: 'Səyahət adapteri',
category: 'Elektronika',
},
{
name: 'Kamera',
category: 'Elektronika',
},
{
name: 'Ağrıkəsici',
category: 'Sağlamlıq',
},
{
name: 'Yara plastırı',
category: 'Sağlamlıq',
},
{
name: 'Dezinfeksiya vasitəsi',
category: 'Sağlamlıq',
},
],
'packing.bagLimit': 'Çəki limiti', 'packing.bagLimit': 'Çəki limiti',
'packing.setBagLimit': 'Limit təyin et', 'packing.setBagLimit': 'Limit təyin et',
-1
View File
@@ -14,7 +14,6 @@ const reservations: TranslationStrings = {
'reservations.pending': 'Gözləyir', 'reservations.pending': 'Gözləyir',
'reservations.summary': 'reservations.summary':
'{confirmed} təsdiqlənib, {pending} gözləyir', '{confirmed} təsdiqlənib, {pending} gözləyir',
'reservations.fromPlan': 'Plandan',
'reservations.showFiles': 'Faylları göstər', 'reservations.showFiles': 'Faylları göstər',
'reservations.editTitle': 'Rezervasiyanı redaktə et', 'reservations.editTitle': 'Rezervasiyanı redaktə et',
+21
View File
@@ -286,6 +286,27 @@ const roadtrip: TranslationStrings = {
'roadtrip.ride.open': 'Rezervasiyanı aç', 'roadtrip.ride.open': 'Rezervasiyanı aç',
'roadtrip.ride.undated': '{title} bu səyahətin heç bir gününə təyin edilmədiyi üçün yol ondan istifadə etmir.', 'roadtrip.ride.undated': '{title} bu səyahətin heç bir gününə təyin edilmədiyi üçün yol ondan istifadə etmir.',
'roadtrip.bookings.loose': 'Bu gün üçün digər rezervasiyalar', 'roadtrip.bookings.loose': 'Bu gün üçün digər rezervasiyalar',
'roadtrip.ride.departureFlight': 'Yola düşmə {time}',
'roadtrip.ride.duration': 'Rezervasiyaya görə müddət',
'roadtrip.ride.checkIn': 'Giriş',
'roadtrip.ride.boarding': 'Minmə',
'roadtrip.ride.missed': 'Qaçırılıb',
'roadtrip.ride.lateBy': '{time} gecikmə',
'roadtrip.ride.reachedAt': 'saat {time} orada',
'roadtrip.ride.hintFlight': '{title} saat {departs} yola düşür, giriş saat {pin} bağlanır.',
'roadtrip.ride.hintShip': '{title} saat {departs} limandan ayrılır, giriş saat {pin} bağlanır.',
'roadtrip.ride.hintBoarding': '{title} saat {departs} yola düşür, minmə saat {pin} bitir.',
'roadtrip.ride.lateHintFlight':
'Maşınla saat {time} {place} ünvanına çatırsınız. {title} saat {departs} yola düşür, giriş saat {pin} bağlanır.',
'roadtrip.ride.lateHintShip':
'Maşınla saat {time} {place} ünvanına çatırsınız. {title} saat {departs} limandan ayrılır, giriş saat {pin} bağlanır.',
'roadtrip.ride.lateHintBoarding':
'Maşınla saat {time} {place} ünvanına çatırsınız. {title} saat {departs} yola düşür, minmə saat {pin} bitir.',
'roadtrip.day.rideMissed.flight': 'Uçuş qaçırılıb',
'roadtrip.day.rideMissed.train': 'Qatar qaçırılıb',
'roadtrip.day.rideMissed.ferry': 'Bərə qaçırılıb',
'roadtrip.day.rideMissed.cruise': 'Kruiz qaçırılıb',
'roadtrip.day.rideMissed.bus': 'Avtobus qaçırılıb',
'roadtrip.bookend.checkOut': 'Çıxış · {name}', 'roadtrip.bookend.checkOut': 'Çıxış · {name}',
'roadtrip.bookend.from': '{name} məkanından', 'roadtrip.bookend.from': '{name} məkanından',
'roadtrip.bookend.back': '{name} məkanına qayıt', 'roadtrip.bookend.back': '{name} məkanına qayıt',
+1 -1
View File
@@ -101,7 +101,7 @@ const trip: TranslationStrings = {
'transit.searchHint': 'transit.searchHint':
'Real bağlantıları axtarın və birbaşa günə əlavə edin — məlumatlar Transitous tərəfindən təmin edilir.', 'Real bağlantıları axtarın və birbaşa günə əlavə edin — məlumatlar Transitous tərəfindən təmin edilir.',
'trip.confirm.deletePlaceNight': 'trip.confirm.deletePlaceNight':
'Bu, həmçinin “{name}” məkanında rezervasiya edilmiş qalmanı silir.', 'Bu, həmçinin “{name}” məkanında rezervasiya edilmiş qalmanı silir.',
'trip.confirm.deletePlaceBooked': 'trip.confirm.deletePlaceBooked':
'Bu, həmçinin “{name}” məkanında rezervasiya edilmiş qalmanı, “{booking}” rezervasiyasını və onunla əlaqəli bütün xərcləri silir.', 'Bu, həmçinin “{name}” məkanında rezervasiya edilmiş qalmanı, “{booking}” rezervasiyasını və onunla əlaqəli bütün xərcləri silir.',
@@ -1,4 +1,5 @@
import ar from './ar'; import ar from './ar';
import az from './az';
import br from './br'; import br from './br';
import ca from './ca'; import ca from './ca';
import cs from './cs'; import cs from './cs';
@@ -39,6 +40,7 @@ import { describe, it, expect } from 'vitest';
*/ */
const LOCALES: Record<string, TranslationStrings> = { const LOCALES: Record<string, TranslationStrings> = {
ar, ar,
az,
br, br,
ca, ca,
cs, cs,
+1
View File
@@ -112,6 +112,7 @@ You can set `DEFAULT_LANGUAGE` to any of the 23 languages TREK ships. The curren
| `it` | Italiano | | `it` | Italiano |
| `tr` | Türkçe | | `tr` | Türkçe |
| `ar` | العربية | | `ar` | العربية |
| `az` | Azərbaycanca |
| `id` | Bahasa Indonesia | | `id` | Bahasa Indonesia |
| `ja` | 日本語 | | `ja` | 日本語 |
| `ko` | 한국어 | | `ko` | 한국어 |
+1
View File
@@ -21,6 +21,7 @@ TREK ships with translations for 23 languages. You can change your language at a
| `it` | Italiano | | `it` | Italiano |
| `tr` | Türkçe | | `tr` | Türkçe |
| `ar` | العربية | | `ar` | العربية |
| `az` | Azərbaycanca |
| `id` | Bahasa Indonesia | | `id` | Bahasa Indonesia |
| `ja` | 日本語 | | `ja` | 日本語 |
| `ko` | 한국어 | | `ko` | 한국어 |