Gogs 2a38f23eb6 -- 3 nedēļas atpakaļ
..
README.md 2a38f23eb6 -- 3 nedēļas atpakaļ
index.coffee 82c59eea6c -- 3 nedēļas atpakaļ
index.pug 82c59eea6c -- 3 nedēļas atpakaļ
index.styl 82c59eea6c -- 3 nedēļas atpakaļ

README.md

Компонент LanguageSwitcher - Полная документация

Назначение компонента

LanguageSwitcher - компонент для переключения языков интерфейса в мультиязычном приложении. Интегрируется с глобальной системой мультиязычности, обеспечивает удобный интерфейс выбора языка и сохраняет предпочтения пользователя.

git репозитарий

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

Полный список props

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 (опциональный)

  • Тип: Boolean
  • По умолчанию: true
  • Описание: Показывать названия на родном языке
  • Пример:

    language-switcher(:showNativeNames="false")
    

showFlags (опциональный)

  • Тип: Boolean
  • По умолчанию: true
  • Описание: Показывать флаги (для соответствующих вариантов)
  • Пример:

    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'
  • Описание: Ключ для сохранения в localStorage
  • Пример:

    language-switcher(storageKey="my-app-language")
    

autoClose (опциональный)

  • Тип: Boolean
  • По умолчанию: true
  • Описание: Автоматически закрывать после выбора
  • Пример:

    language-switcher(:autoClose="false")
    

class (опциональный)

  • Тип: String | Object | Array
  • По умолчанию: ''
  • Описание: Дополнительные CSS классы
  • Пример:

    language-switcher(class="border border-gray-300 rounded-lg")
    

События (Events)

@change

  • Описание: Событие смены языка
  • Payload: { language: String, previousLanguage: String }
  • Пример:

    language-switcher(@change="handleLanguageChange")
    

@open

  • Описание: Событие открытия переключателя
  • Payload: None
  • Пример:

    language-switcher(@open="handleLanguageSwitcherOpen")
    

@close

  • Описание: Событие закрытия переключателя
  • Payload: None
  • Пример:

    language-switcher(@close="handleLanguageSwitcherClose")
    

Слоты (Slots)

[button-content]

  • Описание: Кастомное содержимое кнопки переключателя
  • Пример:

    language-switcher
      template([button-content])
          div(class="flex items-center space-x-2")
              icon(name="globe")
              span Язык
    

[language-option]

  • Описание: Кастомное отображение опции языка
  • Scope: { 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)

  • Название: Russian
  • Родное название: Русский
  • Флаг: 🇷🇺

Английский (en)

  • Название: English
  • Родное название: English
  • Флаг: 🇺🇸

Таджикский (tj)

  • Название: Tajik
  • Родное название: Тоҷикӣ
  • Флаг: 🇹🇯

Примеры использования

1. Базовые варианты отображения

//- Выпадающий список (по умолчанию)
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")

2. Кастомные языки

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: '🇫🇷' }
    ]"
)

3. Интеграция с навигацией

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")

4. В мобильном меню

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"
            )

5. С кастомными слотами

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"
            )

6. С обработкой событий

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()

Методы API

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

# Компонент автоматически интегрируется с 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')

Стили и кастомизация

CSS-классы компонента

.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 - не сохранять снова
)

Интеграция с SEO

methods:
    updateHtmlLang: (language) ->
        # Обновление атрибута lang у html элемента
        document.documentElement.lang = language
        
        # Обновление hreflang meta-тегов
        @updateHreflangTags(language)

Обработка ошибок

Валидация языка

# Проверка поддерживаемых языков
if !@availableLanguages.find((lang) -> lang.code == language)
    throw new Error("Неподдерживаемый язык: "+language)

Fallback для localStorage

try
    localStorage.setItem(@storageKey, language)
catch error
    # Используем cookie как fallback
    document.cookie = @storageKey+"="+language+"; path=/; max-age=31536000"

Примеры интеграции

1. С аналитикой

methods:
    handleLanguageChange: ({ language, previousLanguage }) ->
        # Отправка в аналитику
        gtag('event', 'language_change', {
            event_category: 'ui',
            event_label: language,
            value: 1
        })

2. С синхронизацией с сервером

methods:
    handleLanguageChange: (language) ->
        # Сохранение на сервере (если пользователь авторизован)
        if @user
            await @api.saveUserPreferences({ language: language })

3. С динамической загрузкой переводов

methods:
    handleLanguageChange: (language) ->
        # Загрузка файлов переводов
        try
            translations = await import('../locales/'+language+'.json')
            AppDB.multilingual.setTranslations(language, translations)
        catch error
            debug.log "Ошибка загрузки переводов: "+error

Best Practices

1. Всегда предоставляйте понятные названия

//- Правильно - показываем родные названия
language-switcher(showNativeNames="true")

//- Неправильно - только коды
language-switcher(:showNativeNames="false")

2. Используйте флаги аккуратно

//- Флаги помогают визуальной идентификации
language-switcher(showFlags="true")

//- Но могут быть спорными для некоторых языков

3. Сохраняйте состояние пользователя

//- Автоматически сохраняет в localStorage
language-switcher

4. Обеспечивайте доступность

//- Правильные ARIA-атрибуты
language-switcher(aria-label="Переключение языка")

5. Тестируйте все варианты

# В тестах
describe('LanguageSwitcher', ->
    it('should switch between languages', ->
        switcher.setLanguage('en')
        expect(switcher.getCurrentLanguage()).toBe('en')
        
        switcher.setLanguage('ru')
        expect(switcher.getCurrentLanguage()).toBe('ru')
    )
)

6. Учитывайте RTL языки

methods:
    handleLanguageChange: (language) ->
        # Для RTL языков (арабский, иврит и т.д.)
        if @isRtlLanguage(language)
            document.documentElement.dir = 'rtl'
        else
            document.documentElement.dir = 'ltr'

Компонент LanguageSwitcher предоставляет полнофункциональное решение для управления языками в мультиязычном приложении с поддержкой доступности, сохранения состояния и интеграции с глобальной системой мультиязычности.