Gogs 3 周之前
父节点
当前提交
6a77ea495b
共有 1 个文件被更改,包括 681 次插入0 次删除
  1. 681 0
      vue/app/shared/LanguageSwitcher/README.md

+ 681 - 0
vue/app/shared/LanguageSwitcher/README.md

@@ -0,0 +1,681 @@
+# Компонент LanguageSwitcher - Полная документация
+
+## Назначение компонента
+
+`LanguageSwitcher` - компонент для переключения языков интерфейса в мультиязычном приложении. Интегрируется с глобальной системой мультиязычности, обеспечивает удобный интерфейс выбора языка и сохраняет предпочтения пользователя.
+
+## Импорт и регистрация
+
+```coffee
+# В основном файле приложения (app/temp.coffee)
+components:
+    'language-switcher': require 'app/shared/LanguageSwitcher'
+```
+
+## Базовое использование
+
+```pug
+//- Минимальное использование
+language-switcher
+
+//- В навигационной панели
+div(class="flex items-center space-x-4")
+    language-switcher
+    themetoggle
+```
+
+## Полный список props
+
+### `size` (опциональный)
+- **Тип:** `String`
+- **По умолчанию:** `'md'`
+- **Допустимые значения:** `'sm'`, `'md'`, `'lg'`
+- **Описание:** Размер переключателя
+- **Пример:**
+  ```pug
+  language-switcher(size="sm")
+  language-switcher(size="lg")
+  ```
+
+### `variant` (опциональный)
+- **Тип:** `String`
+- **По умолчанию:** `'dropdown'`
+- **Допустимые значения:** `'dropdown'`, `'buttons'`, `'select'`, `'flags'`
+- **Описание:** Вариант отображения переключателя
+- **Пример:**
+  ```pug
+  language-switcher(variant="buttons")
+  language-switcher(variant="flags")
+  ```
+
+### `showNativeNames` (опциональный)
+- **Тип:** `Boolean`
+- **По умолчанию:** `true`
+- **Описание:** Показывать названия на родном языке
+- **Пример:**
+  ```pug
+  language-switcher(:showNativeNames="false")
+  ```
+
+### `showFlags` (опциональный)
+- **Тип:** `Boolean`
+- **По умолчанию:** `true`
+- **Описание:** Показывать флаги (для соответствующих вариантов)
+- **Пример:**
+  ```pug
+  language-switcher(:showFlags="false")
+  ```
+
+### `position` (опциональный)
+- **Тип:** `String`
+- **По умолчанию:** `'bottom-start'`
+- **Допустимые значения:** `'top'`, `'top-start'`, `'top-end'`, `'bottom'`, `'bottom-start'`, `'bottom-end'`, `'left'`, `'right'`
+- **Описание:** Позиция выпадающего списка
+- **Пример:**
+  ```pug
+  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: '🇹🇯' }]`
+- **Описание:** Список доступных языков
+- **Пример:**
+  ```pug
+  language-switcher(:languages="customLanguages")
+  ```
+
+### `storageKey` (опциональный)
+- **Тип:** `String`
+- **По умолчанию:** `'borbad-language'`
+- **Описание:** Ключ для сохранения в localStorage
+- **Пример:**
+  ```pug
+  language-switcher(storageKey="my-app-language")
+  ```
+
+### `autoClose` (опциональный)
+- **Тип:** `Boolean`
+- **По умолчанию:** `true`
+- **Описание:** Автоматически закрывать после выбора
+- **Пример:**
+  ```pug
+  language-switcher(:autoClose="false")
+  ```
+
+### `class` (опциональный)
+- **Тип:** `String | Object | Array`
+- **По умолчанию:** `''`
+- **Описание:** Дополнительные CSS классы
+- **Пример:**
+  ```pug
+  language-switcher(class="border border-gray-300 rounded-lg")
+  ```
+
+## События (Events)
+
+### `@change`
+- **Описание:** Событие смены языка
+- **Payload:** `{ language: String, previousLanguage: String }`
+- **Пример:**
+  ```pug
+  language-switcher(@change="handleLanguageChange")
+  ```
+
+### `@open`
+- **Описание:** Событие открытия переключателя
+- **Payload:** `None`
+- **Пример:**
+  ```pug
+  language-switcher(@open="handleLanguageSwitcherOpen")
+  ```
+
+### `@close`
+- **Описание:** Событие закрытия переключателя
+- **Payload:** `None`
+- **Пример:**
+  ```pug
+  language-switcher(@close="handleLanguageSwitcherClose")
+  ```
+
+## Слоты (Slots)
+
+### `[button-content]`
+- **Описание:** Кастомное содержимое кнопки переключателя
+- **Пример:**
+  ```pug
+  language-switcher
+      template([button-content])
+          div(class="flex items-center space-x-2")
+              icon(name="globe")
+              span Язык
+  ```
+
+### `[language-option]`
+- **Описание:** Кастомное отображение опции языка
+- **Scope:** `{ language: Object, isActive: Boolean }`
+- **Пример:**
+  ```pug
+  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]`
+- **Описание:** Контент перед списком языков
+- **Пример:**
+  ```pug
+  language-switcher
+      template([before-options])
+          div(class="p-3 border-b border-gray-200 dark:border-gray-700")
+              h3(class="font-semibold") Выберите язык
+  ```
+
+### `[after-options]`
+- **Описание:** Контент после списка языков
+- **Пример:**
+  ```pug
+  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. Базовые варианты отображения
+```pug
+//- Выпадающий список (по умолчанию)
+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. Кастомные языки
+```pug
+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. Интеграция с навигацией
+```pug
+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. В мобильном меню
+```pug
+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. С кастомными слотами
+```pug
+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. С обработкой событий
+```pug
+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 "Переключатель языка закрыт"
+```
+
+## Программное взаимодействие
+
+### Получение текущего языка
+```coffee
+# Через глобальный объект
+currentLanguage = _.currentLanguage
+
+# Через AppDB
+currentLanguage = AppDB.multilingual.currentLanguage
+
+# Через EventBus
+EventBus.on 'language_changed', (language) ->
+    debug.log "Текущий язык: "+language
+```
+
+### Установка языка программно
+```coffee
+# Через глобальный объект
+_.changeLanguage('en')
+
+# Через AppDB
+AppDB.multilingual.setLanguage('tj')
+
+# Через EventBus
+EventBus.emit('set_language', 'ru')
+```
+
+### Подписка на изменения языка
+```coffee
+# В компоненте
+beforeMount: ->
+    EventBus.on 'language_changed', @handleLanguageChange
+
+methods:
+    handleLanguageChange: (language) ->
+        # Перезагрузка данных на новом языке
+        @loadDataForLanguage(language)
+        
+        # Обновление контента
+        @updateContent()
+```
+
+## Методы API
+
+### `setLanguage(languageCode)`
+- **Описание:** Устанавливает указанный язык
+- **Параметры:** `languageCode` - код языка ('ru', 'en', 'tj')
+- **Пример:**
+  ```coffee
+  @$refs.languageSwitcher.setLanguage('en')
+  ```
+
+### `getCurrentLanguage()`
+- **Описание:** Возвращает текущий активный язык
+- **Возвращает:** `String`
+- **Пример:**
+  ```coffee
+  currentLang = @$refs.languageSwitcher.getCurrentLanguage()
+  ```
+
+### `open()`
+- **Описание:** Программно открывает переключатель
+- **Пример:**
+  ```coffee
+  @$refs.languageSwitcher.open()
+  ```
+
+### `close()`
+- **Описание:** Программно закрывает переключатель
+- **Пример:**
+  ```coffee
+  @$refs.languageSwitcher.close()
+  ```
+
+### `getAvailableLanguages()`
+- **Описание:** Возвращает список доступных языков
+- **Возвращает:** `Array`
+- **Пример:**
+  ```coffee
+  languages = @$refs.languageSwitcher.getAvailableLanguages()
+  ```
+
+## Интеграция с мультиязычной системой
+
+### С AppDB
+```coffee
+# Компонент автоматически интегрируется с AppDB.multilingual
+languageSwitcher.setLanguage('en')
+# Эквивалентно:
+AppDB.multilingual.setLanguage('en')
+```
+
+### С глобальным состоянием
+```coffee
+# Изменение языка через компонент обновляет глобальное состояние
+_.changeLanguage('tj')
+```
+
+### С загрузкой данных
+```coffee
+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-классы компонента
+```styl
+.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
+```
+
+### Кастомные стили
+```styl
+// Переопределение стандартных стилей
+.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
+```
+
+### Темная тема
+```styl
+@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
+```
+
+## Особенности работы
+
+### Определение языка по умолчанию
+```coffee
+# Приоритеты определения языка:
+# 1. Сохраненный выбор пользователя (localStorage)
+# 2. Язык браузера
+# 3. Язык по умолчанию (ru)
+```
+
+### Синхронизация между вкладками
+```coffee
+# Слушает события storage для синхронизации
+window.addEventListener('storage', (event) ->
+    if event.key == @storageKey
+        @setLanguage(event.newValue, false) # false - не сохранять снова
+)
+```
+
+### Интеграция с SEO
+```coffee
+methods:
+    updateHtmlLang: (language) ->
+        # Обновление атрибута lang у html элемента
+        document.documentElement.lang = language
+        
+        # Обновление hreflang meta-тегов
+        @updateHreflangTags(language)
+```
+
+## Обработка ошибок
+
+### Валидация языка
+```coffee
+# Проверка поддерживаемых языков
+if !@availableLanguages.find((lang) -> lang.code == language)
+    throw new Error("Неподдерживаемый язык: "+language)
+```
+
+### Fallback для localStorage
+```coffee
+try
+    localStorage.setItem(@storageKey, language)
+catch error
+    # Используем cookie как fallback
+    document.cookie = @storageKey+"="+language+"; path=/; max-age=31536000"
+```
+
+## Примеры интеграции
+
+### 1. С аналитикой
+```coffee
+methods:
+    handleLanguageChange: ({ language, previousLanguage }) ->
+        # Отправка в аналитику
+        gtag('event', 'language_change', {
+            event_category: 'ui',
+            event_label: language,
+            value: 1
+        })
+```
+
+### 2. С синхронизацией с сервером
+```coffee
+methods:
+    handleLanguageChange: (language) ->
+        # Сохранение на сервере (если пользователь авторизован)
+        if @user
+            await @api.saveUserPreferences({ language: language })
+```
+
+### 3. С динамической загрузкой переводов
+```coffee
+methods:
+    handleLanguageChange: (language) ->
+        # Загрузка файлов переводов
+        try
+            translations = await import('../locales/'+language+'.json')
+            AppDB.multilingual.setTranslations(language, translations)
+        catch error
+            debug.log "Ошибка загрузки переводов: "+error
+```
+
+## Best Practices
+
+### 1. Всегда предоставляйте понятные названия
+```pug
+//- Правильно - показываем родные названия
+language-switcher(showNativeNames="true")
+
+//- Неправильно - только коды
+language-switcher(:showNativeNames="false")
+```
+
+### 2. Используйте флаги аккуратно
+```pug
+//- Флаги помогают визуальной идентификации
+language-switcher(showFlags="true")
+
+//- Но могут быть спорными для некоторых языков
+```
+
+### 3. Сохраняйте состояние пользователя
+```pug
+//- Автоматически сохраняет в localStorage
+language-switcher
+```
+
+### 4. Обеспечивайте доступность
+```pug
+//- Правильные ARIA-атрибуты
+language-switcher(aria-label="Переключение языка")
+```
+
+### 5. Тестируйте все варианты
+```coffee
+# В тестах
+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 языки
+```coffee
+methods:
+    handleLanguageChange: (language) ->
+        # Для RTL языков (арабский, иврит и т.д.)
+        if @isRtlLanguage(language)
+            document.documentElement.dir = 'rtl'
+        else
+            document.documentElement.dir = 'ltr'
+```
+
+Компонент LanguageSwitcher предоставляет полнофункциональное решение для управления языками в мультиязычном приложении с поддержкой доступности, сохранения состояния и интеграции с глобальной системой мультиязычности.