Diffstat (limited to 'l10n')
| -rw-r--r-- | l10n/en.yaml | 79 | ||||
| -rw-r--r-- | l10n/ru.yaml | 79 | ||||
| -rw-r--r-- | l10n/translator-hook.go | 39 | ||||
| -rw-r--r-- | l10n/translator.go | 167 |
4 files changed, 0 insertions, 364 deletions
diff --git a/l10n/en.yaml b/l10n/en.yaml deleted file mode 100644 index 9571aca..0000000 --- a/l10n/en.yaml +++ /dev/null @@ -1,79 +0,0 @@ -TagsLabel: "Tags:" -BlogSearch: - Header: "Blog Search" - Description: "The Saya Blog posts' list" - TagsHeader: "Tags" - OrderByHeader: "Order by..." - TitleOrdered: "Title" - ActionDateOrdered: "Action Date" - PublicationDateOrdered: "Publication Date" - ChooseAllTags: "Choose All" -GlobalMap: - Header: "Global Map" - Description: "Global map with all the tags of places I've been to" -HomePage: - Header: "Home Page" - Contacts: "Contacts" - DidYouKnowThat: "Did you know, that..." - SidebarDescription: "Describing the Sidebar" - BackDescription: "This button will lead back to higher page (like Home => Language Pick, Blog Search => Home)" - HomePageDescription: "Home Page -- where we are right now!" - BlogSearchDescription: "Blog Search. You can find my travel notes there and, maybe, something else!" - MarkerMapDescription: "Map with markers of my travel notes -- a convinient way to find an interesting region and find related posts!" - ThemeSwitchDescription: "Theme Palette lets you change the theme of the site -- there are 2 dark and 3 bright themes currently!" - AccountDescription: "This is where you can subscribe to any blog update via E-mail!" - Hymn1: "We welcome you to Sayasite!" - Hymn2: "We've been expecting you!" - Hymn3: "You bring such joy in Sayasite!" - Hymn4: "No matter where you roam, know our love is true!~" -UnsubscribePage: - Header: "Cancelling the Subscription" - UnsetCode: "Your cancel code is not set. You just having a walk around the site?" - InvalidCode: "Your cancel code could not be verified. Maybe, the code expired after a day. Or maybe, you've already unsubscribed." - OnServerError: "We couldn't unsubscribe you, the problem is on our side! We will solve this problem as soon as possible." - Success: "Cancelled your subscription successfully! We do hope we will engage you again someday." -Metadata: - Published: "Published" - Action: "Took place on" -Mail: - UnsubscribeFooter: "If this letter got you in a bad mood, you can unsubscribe from my blog by {}this link{/}." - VerifyEmail: - Subject: "Verification Link for SAYA.UZ is waiting for you!" - Welcome: "Greetings!" - Intro: "There are 2 ways for us to validate your profile:" - GotoLink: "Go by this link:" - InputCode: "Or input this code on the personal page:" - IfRandom: "If you are unsure what this letter this, you can ignore it, and also, if you are interested, you may visit my site :)" - NewPost: - Subject: "A new post arrived at SAYA.UZ!" - Intro: "A new post came!" - CapturedOn: "Captured on" -UserProfile: - Header: "Personal Settings" - Description: "Set and verify your e-mail here, as well as subscribe to favorite blog tags" - EmailHeader: "E-Mail" - VerificationCodeHeader: "Verification Code" - TypeEmail: "Please type your email" - SendCodeButton: "Send Code" - VerifyButton: "Verify" - VerificationCodeSent: "Verification code sent! Please check your e-mail. Your next verification attempt will be possible on {}." - DelayTilVerification: "You have already requested the verification before! Please wait until {} for a new verification request." - FailedEmailRender: "Failed to render the email status message, please ask administrator for a more detailed cause." - VerificationCodeSendingError: "Failed to send a verification code, please ask administrator for a more detailed cause." - EmailAlreadyValidated: "You already have this E-Mail validated." - EmailTaken: "This E-Mail is already taken." - EmailEmpty: "E-Mail is unset or empty." - VerificationSuccess: "Success! You are now able to subscribe with your new e-mail." - VerificationFailed: "Verification code is invalid, please ask administrator for a more detailed cause." - VerificationEmpty: "Verification code is unset or empty." - SubscriptionHeader: "Subscribe to..." - TagsNone: "None" - TagsAll: "All" - TagsSpecific: "Specific" - TagDoesNotExist: 'The tag "{}" does not exist on the site.' - TagAlreadyAdded: 'The tag "{}" is already in your subscribed list.' - SaveButton: "Save" - SubscribeInvalidType: "Invalid subscription type." - FailedToSubscribe: "Failed to subscribe by new settings, please ask administrator for a more detailed cause." - SubscribedSuccessfully: "Successfully subscribed you by new settings!" - RefreshPage: "Please refresh the profile page on the device, where you requested the code." diff --git a/l10n/ru.yaml b/l10n/ru.yaml deleted file mode 100644 index 1818998..0000000 --- a/l10n/ru.yaml +++ /dev/null @@ -1,79 +0,0 @@ -TagsLabel: "Тэги:" -BlogSearch: - Header: "Поиск по блогу" - Description: "Каталог записей Saya Blog" - TagsHeader: "Тэги" - OrderByHeader: "Упорядочить по..." - TitleOrdered: "названию" - ActionDateOrdered: "дате действия" - PublicationDateOrdered: "времени публикации" - ChooseAllTags: "Выбрать все" -GlobalMap: - Header: "Глобальная карта" - Description: "Глобальная карта с метками мест, где я побывал" -HomePage: - Header: "Домашняя страница" - Contacts: "Контакты" - DidYouKnowThat: "А вы знали, что..." - SidebarDescription: "Поясняем за сайдбар" - BackDescription: "Эта кнопка позволяет идти на страницу вверх (т.е. Домашняя страница => Выбор языка, Поиск по блогу => Домашняя страница)" - HomePageDescription: "Домашняя страница -- где мы сейчас и находимся!" - BlogSearchDescription: "Поиск по блогу. Там размещены мои путевые заметки и, может, что-то ещё!" - MarkerMapDescription: "Карта с маркерами моих путевых заметок, -- удобный способ найти интересный регион и перейти на заинтересовавшие посты!" - ThemeSwitchDescription: "Позволяет поменять тему сайта -- есть 3 светлые и 2 тёмные темы!" - AccountDescription: "Здесь можно подписаться на обновления в блоге, используя электронную почту!" - Hymn1: "Приветствуем Вас на Саясайте!" - Hymn2: "Мы Вас столько ждали!" - Hymn3: "Столько радости от Вас на Саясайте!" - Hymn4: "Где бы Вы не были, знайте, -- наша любовь крепка!" -UnsubscribePage: - Header: "Отписка" - UnsetCode: "Код для отписки не проставлен. Вы случайно не заблудились?" - InvalidCode: "Код для отписки не подтверждён. Может, истекло время в сутки для кода. А может, вы уже успели отписаться." - OnServerError: "Мы не смогли вас отписать, проблема на нашей стороне! В скором времени займёмся проблемой." - Success: "Вы отписались от рассылки! Надеюсь, мы ещё сможем вас заинтересовать." -Metadata: - Published: "Опубликовано" - Action: "Время действия" -Mail: - UnsubscribeFooter: "Если данное письмо пришло вам случайно, либо вы хотите отписаться, можете перейти по {}этой ссылке{/}." - VerifyEmail: - Subject: "Ссылка для верификации для подтверждения вашей почты SAYA.UZ" - Welcome: "Приветствую!" - Intro: "Есть 2 способа, чтобы подтвердить свою электронную почту:" - GotoLink: "Перейти по этой ссылке:" - InputCode: "Либо ввести этот код на странице подписок:" - IfRandom: "Если ты не понимаешь, что это за спам, можешь проигнорировать это письмо, а ещё, если интересно, можешь посетить мой сайт :)" - NewPost: - Subject: "Новый пост на SAYA.UZ!" - Intro: "Вышел новый пост!" - CapturedOn: "Снималось" -UserProfile: - Header: "Личные настройки" - Description: "Здесь можно настроить и верифицировать свою электронную почту, а также подписаться на избранные темы блога" - EmailHeader: "E-Mail" - VerificationCodeHeader: "Код верификации" - TypeEmail: "Пожалуйста, введите свою почту" - SendCodeButton: "Отправить код" - VerifyButton: "Верифицировать" - VerificationCodeSent: "Выслали вам код верификации! Проверьте свою почту. Следующая попытка верификации будет возможна {}." - DelayTilVerification: "Вы уже запрашивали недавно верификацию! Дождитесь {} для новой попытки." - FailedEmailRender: "Неудачная попытка создать электронное письмо, более подробно спрашивайте у администратора." - VerificationCodeSendingError: "Неудачная попытка прислать код для верификации, более подробно спрашивайте у администратора." - EmailAlreadyValidated: "Эту почту вы уже подтверждали." - EmailTaken: "Эта почта уже занята." - EmailEmpty: "Почта не введена." - VerificationSuccess: "Поздравляем! Теперь по этой почте можно подписаться на блог." - VerificationFailed: "Код для верификации невалиден, при вопросах, пожалуйста, обращайтесь к администратору." - VerificationEmpty: "Код для верификации не введён." - SubscriptionHeader: "Подписаться на..." - TagsNone: "Ничего" - TagsAll: "Всё" - TagsSpecific: "Определённое" - TagDoesNotExist: 'Тэга "{}" нет на сайте.' - TagAlreadyAdded: 'Тэг "{}" уже у вас добавлен.' - SaveButton: "Сохранить" - SubscribeInvalidType: "Неизвестный тип подписки." - FailedToSubscribe: "Неудачная попытка записать новую подписку, более подробно спрашивайте у администратора." - SubscribedSuccessfully: "Вы успешно подписались по новым настройкам!" - RefreshPage: "Пожалуйста, обновите страницу с профилем на устройстве, откуда запрашивали код." diff --git a/l10n/translator-hook.go b/l10n/translator-hook.go deleted file mode 100644 index ff5837c..0000000 --- a/l10n/translator-hook.go +++ /dev/null @@ -1,39 +0,0 @@ -package l10n - -var hooks map[string]*TranslatorHook - -type TranslatorHook struct { - t *Translator - prefix string -} - -func NewTranslatorHook(prefix string) *TranslatorHook { - if _, ok := hooks[prefix]; !ok { - hooks[prefix] = &TranslatorHook{T, prefix} - } - return hooks[prefix] -} - -func (h *TranslatorHook) Set(key string, val any, toOverwrite bool) (alreadySet bool, err error) { - return h.t.Set(h.prefix+key, val, toOverwrite) -} - -func (h *TranslatorHook) SetPath(val any, toOverwrite bool, path ...any) (alreadySet bool, err error) { - return h.t.SetPath(val, toOverwrite, append([]any{h.prefix}, path...)...) -} - -func (h *TranslatorHook) Get(key string) any { - return h.t.Get(h.prefix + "." + key) -} - -func (h *TranslatorHook) GetPath(path ...any) any { - return h.t.GetPath(append([]any{h.prefix}, path...)...) -} - -func (h *TranslatorHook) GetDefault(key string, def any) any { - return h.t.GetDefault(h.prefix+"."+key, def) -} - -func (h *TranslatorHook) GetDefaultPath(def any, path ...any) any { - return h.t.GetDefaultPath(def, append([]any{h.prefix}, path...)...) -} diff --git a/l10n/translator.go b/l10n/translator.go deleted file mode 100644 index 4d4b4eb..0000000 --- a/l10n/translator.go +++ /dev/null @@ -1,167 +0,0 @@ -package l10n - -import ( - "errors" - "fmt" - "log/slog" - "os" - "strings" - "sync" - - "gopkg.in/yaml.v3" -) - -var T *Translator - -func init() { - var err error - if T, err = NewTranslator("ru", "en"); err != nil { - slog.Error("fail to load localization", slog.String("error", err.Error())) - os.Exit(1) - } -} - -type Translator struct { - l map[string]any - mu sync.RWMutex -} - -func NewTranslator(locales ...string) (*Translator, error) { - t := &Translator{l: make(map[string]any)} - errs := make([]error, 0) - - for _, locale := range locales { - contentBytes, err := os.ReadFile(fmt.Sprintf("l10n/%s.yaml", locale)) - if err != nil { - errs = append(errs, fmt.Errorf("opening one of files: l10n/%s.yaml: %w", locale, err)) - continue - } - - var content map[string]any - if err := yaml.Unmarshal(contentBytes, &content); err != nil { - errs = append(errs, fmt.Errorf("parsing one of files: l10n/%s.yaml: %w", locale, err)) - continue - } - - errs = append(errs, t.initMap("."+locale, content)...) - } - - return t, errors.Join(errs...) -} - -func (t *Translator) Set(key string, val any, toOverwrite bool) (alreadySet bool, err error) { - t.mu.RLock() - if _, alreadySet = t.l[key]; !alreadySet || (alreadySet && toOverwrite) { - t.mu.RUnlock() - return alreadySet, errors.Join(t.setSafe(key, val)...) - } - t.mu.RUnlock() - return alreadySet, nil -} - -func (t *Translator) SetPath(val any, toOverwrite bool, path ...any) (alreadySet bool, err error) { - key, err := formatPath(path...) - if err != nil { - return false, err - } - return t.Set(key, val, toOverwrite) -} - -func (t *Translator) Get(key string) any { - return t.GetDefault(key, key[strings.LastIndex(key, ".")+1:]) -} - -func (t *Translator) GetPath(path ...any) any { - return t.GetDefaultPath(path[len(path)-1], path...) -} - -func (t *Translator) GetDefault(key string, def any) any { - t.mu.RLock() - defer t.mu.RUnlock() - if v, ok := t.l[key]; ok { - return v - } - slog.Warn("failed to find localization for a key", slog.String("key", key)) - return def -} - -func (t *Translator) GetDefaultPath(def any, path ...any) any { - t.mu.RLock() - defer t.mu.RUnlock() - key, err := formatPath(path...) - if err != nil { - slog.Error("get localized value by path: path is invalid", slog.String("error", err.Error())) - return def - } - return t.GetDefault(key, def) -} - -func (t *Translator) initSlice(parent string, level []any) []error { - errs := make([]error, 0) - for i, v := range level { - newParent := parent + fmt.Sprintf("[%d]", i) - t.setSafe(newParent, v) - } - return errs -} - -func (t *Translator) initMap(parent string, level map[string]any) []error { - errs := make([]error, 0) - for k, v := range level { - key, err := formatPath(k) - if err != nil { - errs = append(errs, err) - continue - } - - errs = append(errs, t.setSafe(parent+key, v)...) - } - return errs -} - -func (t *Translator) setSafe(k string, v any) []error { - errs := make([]error, 0) - t.mu.Lock() - switch vv := v.(type) { - case int: - t.l[k] = vv - t.mu.Unlock() - case float64: - t.l[k] = vv - t.mu.Unlock() - case string: - t.l[k] = vv - t.mu.Unlock() - case []any: - t.mu.Unlock() - errs = append(errs, t.initSlice(k, vv)...) - case map[string]any: - t.mu.Unlock() - errs = append(errs, t.initMap(k, vv)...) - default: - t.mu.Unlock() - errs = append(errs, fmt.Errorf("invalid value type: %s, %v", k, vv)) - } - return errs -} - -func formatPath(path ...any) (string, error) { - var key string - for _, part := range path { - switch k := part.(type) { - case int: - key += fmt.Sprintf("[%d]", k) - case string: - key += "." + strings.ReplaceAll(k, ".", "\\.") - case []any: - add, err := formatPath(k...) - if err != nil { - return "", fmt.Errorf("while formatting nested slice: %w", err) - } - key += add - default: - return "", fmt.Errorf("invalid key part: after %s follows %v", key, k) - } - } - return key, nil -} |