|
|
3 viikkoa sitten | |
|---|---|---|
| .. | ||
| README.md | 3 viikkoa sitten | |
| index.coffee | 3 viikkoa sitten | |
| index.pug | 3 viikkoa sitten | |
| index.styl | 3 viikkoa sitten | |
LanguageSwitcher - компонент для переключения языков интерфейса в мультиязычном приложении. Интегрируется с глобальной системой мультиязычности, обеспечивает удобный интерфейс выбора языка и сохраняет предпочтения пользователя.
https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/src/master/vue/app/shared/LanguageSwitcher/
# В основном файле приложения (app/temp.coffee)
components:
'language-switcher': require 'app/shared/LanguageSwitcher'
//- Минимальное использование
language-switcher
//- В навигационной панели
div(class="flex items-center space-x-4")
language-switcher
themetoggle
size (опциональный)String'md''sm', 'md', 'lg'Пример:
language-switcher(size="sm")
language-switcher(size="lg")
variant (опциональный)String'dropdown''dropdown', 'buttons', 'select', 'flags'Пример:
language-switcher(variant="buttons")
language-switcher(variant="flags")
showNativeNames (опциональный)BooleantrueПример:
language-switcher(:showNativeNames="false")
showFlags (опциональный)BooleantrueПример:
language-switcher(:showFlags="false")
position (опциональный)String'bottom-start''top', 'top-start', 'top-end', 'bottom', 'bottom-start', 'bottom-end', 'left', 'right'Пример:
language-switcher(position="bottom-end")
languages (опциональный)Array[{ code: 'ru', name: 'Russian', native: 'Русский', flag: '🇷🇺' }, { code: 'en', name: 'English', native: 'English', flag: '🇺🇸' }, { code: 'tj', name: 'Tajik', native: 'Тоҷикӣ', flag: '🇹🇯' }]Пример:
language-switcher(:languages="customLanguages")
storageKey (опциональный)String'borbad-language'Пример:
language-switcher(storageKey="my-app-language")
autoClose (опциональный)BooleantrueПример:
language-switcher(:autoClose="false")
class (опциональный)String | Object | Array''Пример:
language-switcher(class="border border-gray-300 rounded-lg")
@change{ language: String, previousLanguage: String }Пример:
language-switcher(@change="handleLanguageChange")
@openNoneПример:
language-switcher(@open="handleLanguageSwitcherOpen")
@closeNoneПример:
language-switcher(@close="handleLanguageSwitcherClose")
[button-content]Пример:
language-switcher
template([button-content])
div(class="flex items-center space-x-2")
icon(name="globe")
span Язык
[language-option]{ language: Object, isActive: Boolean }Пример:
language-switcher
template([language-option]="{ language, isActive }")
div(class="flex items-center space-x-3 p-2")
span(class="text-lg") {{ language.flag }}
div(class="flex flex-col")
span(class="font-medium") {{ language.native }}
span(class="text-sm text-gray-500") {{ language.name }}
span(
v-if="isActive"
class="text-green-500 ml-auto"
) ✓
[before-options]Пример:
language-switcher
template([before-options])
div(class="p-3 border-b border-gray-200 dark:border-gray-700")
h3(class="font-semibold") Выберите язык
[after-options]Пример:
language-switcher
template([after-options])
div(class="p-3 border-t border-gray-200 dark:border-gray-700")
p(class="text-sm text-gray-500") Помогите с переводом
ru)en)tj)//- Выпадающий список (по умолчанию)
language-switcher
//- Кнопки
language-switcher(variant="buttons")
//- Select элемент
language-switcher(variant="select")
//- Флаги
language-switcher(variant="flags")
//- Разные размеры
div(class="flex space-x-2 items-center")
language-switcher(size="sm")
language-switcher(size="md")
language-switcher(size="lg")
language-switcher(
:languages="[
{ code: 'ru', name: 'Russian', native: 'Русский', flag: '🇷🇺' },
{ code: 'en', name: 'English', native: 'English', flag: '🇺🇸' },
{ code: 'de', name: 'German', native: 'Deutsch', flag: '🇩🇪' },
{ code: 'fr', name: 'French', native: 'Français', flag: '🇫🇷' }
]"
)
header(class="bg-white dark:bg-gray-800 shadow-sm")
div(class="container mx-auto px-4")
div(class="flex justify-between items-center py-4")
//- Логотип
app-link(to="/" class="text-xl font-bold") Борбад
//- Навигация с переключателями
div(class="flex items-center space-x-4")
nav(class="flex space-x-6")
app-link(to="/events") Мероприятия
app-link(to="/blog") Блог
//- Переключатели
language-switcher(
variant="flags"
size="sm"
:showNativeNames="false"
)
themetoggle(variant="icon-only")
div(class="mobile-menu bg-white dark:bg-gray-800 p-6")
div(class="space-y-4")
app-link(to="/" class="block py-2") Главная
app-link(to="/events" class="block py-2") Мероприятия
//- Переключатель языка
div(class="pt-4 border-t dark:border-gray-700")
language-switcher(
variant="buttons"
class="w-full"
)
language-switcher
template([button-content])
div(class="flex items-center space-x-2")
icon(name="translate" class="w-4 h-4")
span {{ _.currentLanguage.toUpperCase() }}
template([before-options])
div(class="p-3 bg-blue-50 dark:bg-blue-900")
p(class="text-sm text-blue-700 dark:text-blue-300") Выберите язык интерфейса
template([language-option]="{ language, isActive }")
div(
:class="[
'flex items-center space-x-3 p-3 rounded-lg transition-colors',
isActive ? 'bg-blue-100 dark:bg-blue-800' : 'hover:bg-gray-100 dark:hover:bg-gray-700'
]"
)
span(class="text-xl") {{ language.flag }}
div(class="flex-1")
div(class="font-medium") {{ language.native }}
div(class="text-sm text-gray-500 dark:text-gray-400") {{ language.name }}
icon(
v-if="isActive"
name="check"
class="w-4 h-4 text-blue-600 dark:text-blue-400"
)
language-switcher(
@change="onLanguageChange"
@open="onLanguageSwitcherOpen"
@close="onLanguageSwitcherClose"
)
//- В methods компонента
methods:
onLanguageChange: ({ language, previousLanguage }) ->
debug.log "Язык изменен: "+previousLanguage+" -> "+language
# Аналитика
analytics.track('language_changed', {
new_language: language,
previous_language: previousLanguage
})
# Обновление мета-тегов
@updateHtmlLang(language)
onLanguageSwitcherOpen: ->
debug.log "Переключатель языка открыт"
analytics.track('language_switcher_opened')
onLanguageSwitcherClose: ->
debug.log "Переключатель языка закрыт"
# Через глобальный объект
currentLanguage = _.currentLanguage
# Через AppDB
currentLanguage = AppDB.multilingual.currentLanguage
# Через EventBus
EventBus.on 'language_changed', (language) ->
debug.log "Текущий язык: "+language
# Через глобальный объект
_.changeLanguage('en')
# Через AppDB
AppDB.multilingual.setLanguage('tj')
# Через EventBus
EventBus.emit('set_language', 'ru')
# В компоненте
beforeMount: ->
EventBus.on 'language_changed', @handleLanguageChange
methods:
handleLanguageChange: (language) ->
# Перезагрузка данных на новом языке
@loadDataForLanguage(language)
# Обновление контента
@updateContent()
setLanguage(languageCode)languageCode - код языка ('ru', 'en', 'tj')Пример:
@$refs.languageSwitcher.setLanguage('en')
getCurrentLanguage()StringПример:
currentLang = @$refs.languageSwitcher.getCurrentLanguage()
open()Пример:
@$refs.languageSwitcher.open()
close()Пример:
@$refs.languageSwitcher.close()
getAvailableLanguages()ArrayПример:
languages = @$refs.languageSwitcher.getAvailableLanguages()
# Компонент автоматически интегрируется с AppDB.multilingual
languageSwitcher.setLanguage('en')
# Эквивалентно:
AppDB.multilingual.setLanguage('en')
# Изменение языка через компонент обновляет глобальное состояние
_.changeLanguage('tj')
methods:
handleLanguageChange: (language) ->
# Перезагрузка контента на новом языке
promises = [
AppDB.getBlogPosts({ language: language })
AppDB.getEvents({ language: language })
AppDB.getCategories({ language: language })
]
await Promise.all(promises)
EventBus.emit('content_updated')
.language-switcher
// Основной контейнер
&__container
@apply relative inline-block
// Кнопка переключателя
&__button
@apply flex items-center space-x-2 px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors
// Выпадающий список
&__dropdown
@apply absolute z-50 mt-2 w-48 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 py-1
// Опция языка
&__option
@apply flex items-center space-x-3 px-4 py-2 text-sm text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors cursor-pointer
&--active
@apply bg-blue-50 dark:bg-blue-900 text-blue-600 dark:text-blue-400
// Вариант с кнопками
&--buttons
@apply flex space-x-1
.language-switcher__button
@apply rounded-none border-r-0
&:first-child
@apply rounded-l-lg
&:last-child
@apply rounded-r-lg border-r
// Вариант с флагами
&--flags
.language-switcher__button
@apply p-1
.language-switcher__option
@apply px-3 py-1
// Переопределение стандартных стилей
.language-switcher--custom
.language-switcher__button
@apply bg-gradient-to-r from-purple-500 to-pink-500 text-white border-transparent
&:hover
@apply from-purple-600 to-pink-600
.language-switcher__option
&:hover
@apply bg-gradient-to-r from-purple-50 to-pink-50 text-purple-700
&--active
@apply bg-gradient-to-r from-purple-100 to-pink-100 text-purple-800
@media (prefers-color-scheme: dark)
.language-switcher
&__button
@apply bg-gray-800 border-gray-600 text-gray-300
&:hover
@apply bg-gray-700
&__dropdown
@apply bg-gray-800 border-gray-700
&__option
@apply text-gray-300
&:hover
@apply bg-gray-700
&--active
@apply bg-blue-900 text-blue-400
# Приоритеты определения языка:
# 1. Сохраненный выбор пользователя (localStorage)
# 2. Язык браузера
# 3. Язык по умолчанию (ru)
# Слушает события storage для синхронизации
window.addEventListener('storage', (event) ->
if event.key == @storageKey
@setLanguage(event.newValue, false) # false - не сохранять снова
)
methods:
updateHtmlLang: (language) ->
# Обновление атрибута lang у html элемента
document.documentElement.lang = language
# Обновление hreflang meta-тегов
@updateHreflangTags(language)
# Проверка поддерживаемых языков
if !@availableLanguages.find((lang) -> lang.code == language)
throw new Error("Неподдерживаемый язык: "+language)
try
localStorage.setItem(@storageKey, language)
catch error
# Используем cookie как fallback
document.cookie = @storageKey+"="+language+"; path=/; max-age=31536000"
methods:
handleLanguageChange: ({ language, previousLanguage }) ->
# Отправка в аналитику
gtag('event', 'language_change', {
event_category: 'ui',
event_label: language,
value: 1
})
methods:
handleLanguageChange: (language) ->
# Сохранение на сервере (если пользователь авторизован)
if @user
await @api.saveUserPreferences({ language: language })
methods:
handleLanguageChange: (language) ->
# Загрузка файлов переводов
try
translations = await import('../locales/'+language+'.json')
AppDB.multilingual.setTranslations(language, translations)
catch error
debug.log "Ошибка загрузки переводов: "+error
//- Правильно - показываем родные названия
language-switcher(showNativeNames="true")
//- Неправильно - только коды
language-switcher(:showNativeNames="false")
//- Флаги помогают визуальной идентификации
language-switcher(showFlags="true")
//- Но могут быть спорными для некоторых языков
//- Автоматически сохраняет в localStorage
language-switcher
//- Правильные ARIA-атрибуты
language-switcher(aria-label="Переключение языка")
# В тестах
describe('LanguageSwitcher', ->
it('should switch between languages', ->
switcher.setLanguage('en')
expect(switcher.getCurrentLanguage()).toBe('en')
switcher.setLanguage('ru')
expect(switcher.getCurrentLanguage()).toBe('ru')
)
)
methods:
handleLanguageChange: (language) ->
# Для RTL языков (арабский, иврит и т.д.)
if @isRtlLanguage(language)
document.documentElement.dir = 'rtl'
else
document.documentElement.dir = 'ltr'
Компонент LanguageSwitcher предоставляет полнофункциональное решение для управления языками в мультиязычном приложении с поддержкой доступности, сохранения состояния и интеграции с глобальной системой мультиязычности.