Skip to content

Commit

Permalink
Add translation system (#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
thomiceli committed Sep 22, 2023
1 parent 61e274e commit a5ea522
Show file tree
Hide file tree
Showing 27 changed files with 774 additions and 205 deletions.
125 changes: 125 additions & 0 deletions internal/i18n/locale.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package i18n

import (
"fmt"
"github.com/thomiceli/opengist/internal/i18n/locales"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"golang.org/x/text/language/display"
"gopkg.in/yaml.v3"
"html/template"
"io"
"io/fs"
"path/filepath"
"strings"
)

var title = cases.Title(language.English)
var Locales = NewLocaleStore()

type LocaleStore struct {
Locales map[string]*Locale
}

type Locale struct {
Code string
Name string
Messages map[string]string
}

// NewLocaleStore creates a new LocaleStore
func NewLocaleStore() *LocaleStore {
return &LocaleStore{
Locales: make(map[string]*Locale),
}
}

// loadLocaleFromYAML loads a single Locale from a given YAML file
func (store *LocaleStore) loadLocaleFromYAML(localeCode, path string) error {
a, err := locales.Files.Open(path)
if err != nil {
return err
}
data, err := io.ReadAll(a)
if err != nil {
return err
}

tag, err := language.Parse(localeCode)
if err != nil {
return err
}

name := display.Self.Name(tag)
if tag == language.AmericanEnglish {
name = "English"
}

locale := &Locale{
Code: localeCode,
Name: title.String(name),
Messages: make(map[string]string),
}

err = yaml.Unmarshal(data, &locale.Messages)
if err != nil {
return err
}

store.Locales[localeCode] = locale
return nil
}

func (store *LocaleStore) LoadAll() error {
return fs.WalkDir(locales.Files, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() {
localeKey := strings.TrimSuffix(path, filepath.Ext(path))
err := store.loadLocaleFromYAML(localeKey, path)
if err != nil {
return err
}
}
return nil
})
}

func (store *LocaleStore) GetLocale(lang string) (*Locale, error) {
_, ok := store.Locales[lang]
if !ok {
return nil, fmt.Errorf("locale %s not found", lang)
}

return store.Locales[lang], nil
}

func (store *LocaleStore) HasLocale(lang string) bool {
_, ok := store.Locales[lang]
return ok
}

func (store *LocaleStore) MatchTag(langs []language.Tag) string {
for _, lang := range langs {
if store.HasLocale(lang.String()) {
return lang.String()
}
}

return "en-US"
}

func (l *Locale) Tr(key string, args ...any) template.HTML {
message := l.Messages[key]

if message == "" {
return Locales.Locales["en-US"].Tr(key, args...)
}

if len(args) == 0 {
return template.HTML(message)
}

return template.HTML(fmt.Sprintf(message, args...))
}
177 changes: 177 additions & 0 deletions internal/i18n/locales/en-US.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
gist.public: Public
gist.unlisted: Unlisted
gist.private: Private

gist.header.like: Like
gist.header.unlike: Unlike
gist.header.fork: Fork
gist.header.edit: Edit
gist.header.delete: Delete
gist.header.forked-from: Forked from
gist.header.last-active: Last active
gist.header.select-tab: Select a tab
gist.header.code: Code
gist.header.revisions: Revisions
gist.header.revision: Revision
gist.header.clone-http: Clone via %s
gist.header.clone-http-help: Clone with Git using HTTP basic authentication.
gist.header.clone-ssh: Clone via SSH
gist.header.clone-ssh-help: Clone with Git using an SSH key.
gist.header.share: Share
gist.header.share-help: Copy shareable link for this gist.
gist.header.download-zip: Download ZIP

gist.raw: Raw
gist.file-truncated: This file has been truncated.
gist.watch-full-file: View the full file.
gist.file-not-valid: This file is not a valid CSV file.
gist.no-content: No content

gist.new.new_gist: New gist
gist.new.title: Title
gist.new.description: Description
gist.new.filename-with-extension: Filename with extension
gist.new.indent-mode: Indent mode
gist.new.indent-mode-space: Space
gist.new.indent-mode-tab: Tab
gist.new.indent-size: Indent size
gist.new.wrap-mode: Wrap mode
gist.new.wrap-mode-no: No wrap
gist.new.wrap-mode-soft: Soft wrap
gist.new.add-file: Add file
gist.new.create-public-button: Create public gist
gist.new.create-unlisted-button: Create unlisted gist
gist.new.create-private-button: Create private gist

gist.edit.editing: Editing
gist.edit.change-visibility: Make
gist.edit.delete: Delete
gist.edit.cancel: Cancel
gist.edit.save: Save

gist.list.joined: Joined
gist.list.all: All gists
gist.list.search-results: Search results
gist.list.sort: Sort
gist.list.sort-by-created: created
gist.list.sort-by-updated: updated
gist.list.order-by-asc: Least recently
gist.list.order-by-desc: Recently
gist.list.select-tab: Select a tab
gist.list.liked: Liked
gist.list.likes: likes
gist.list.forked: Forked
gist.list.forked-from: Forked from
gist.list.forks: forks
gist.list.files: files
gist.list.last-active: Last active
gist.list.no-gists: No gists

gist.forks: Forks
gist.forks.view: View fork
gist.forks.no: No public forks

gist.likes: Likes
gist.likes.no: No likes yet

gist.revisions: Revisions
gist.revision.revised: revised this gist
gist.revision.go-to-revision: Go to revision
gist.revision.file-created: file created
gist.revision.file-deleted: file deleted
gist.revision.file-renamed: renamed to
gist.revision.diff-truncated: Diff truncated because it's too large to be shown
gist.revision.file-renamed-no-changes: File renamed without changes
gist.revision.empty-file: Empty file
gist.revision.no-changes: No changes
gist.revision.no-revisions: No revisions to show

settings: Settings
settings.email: Email
settings.email-help: Used for commits and Gravatar
settings.email-set: Set email
settings.link-accounts: Link accounts
settings.link-github-account: Link GitHub account
settings.link-gitea-account: Link Gitea account
settings.unlink-github-account: Unlink GitHub account
settings.unlink-gitea-account: Unlink Gitea account
settings.delete-account: Delete account
settings.delete-account-confirm: Are you sure you want to delete your account ?
settings.add-ssh-key: Add SSH key
settings.add-ssh-key-help: Used only to pull/push gists using Git via SSH
settings.add-ssh-key-title: Title
settings.add-ssh-key-content: Key
settings.delete-ssh-key: Delete
settings.delete-ssh-key-confirm: Confirm deletion of SSH key
settings.ssh-key-added-at: Added
settings.ssh-key-never-used: Never used
settings.ssh-key-last-used: Last used

auth.signup-disabled: Administrator has disabled signing up
auth.login: Login
auth.signup: Register
auth.new-account: New account
auth.username: Username
auth.password: Password
auth.register-instead: Register instead
auth.login-instead: Login instead
auth.github-oauth: Continue with GitHub account
auth.gitea-oauth: Continue with Gitea account

error: Error

header.menu.all: All
header.menu.new: New
header.menu.search: Search
header.menu.my-gists: My gists
header.menu.liked: Liked
header.menu.admin: Admin
header.menu.settings: Settings
header.menu.logout: Logout
header.menu.register: Register
header.menu.login: Login
header.menu.light: Light
header.menu.dark: Dark
header.menu.system: System
footer.powered-by: Powered by %s

pagination.older: Older
pagination.newer: Newer
pagination.previous: Previous
pagination.next: Next

admin.admin_panel: Admin panel
admin.general: General
admin.users: Users
admin.gists: Gists
admin.configuration: Configuration
admin.versions: Versions
admin.ssh_keys: SSH keys
admin.stats: Stats
admin.actions: Actions
admin.actions.sync-fs: Synchronize gists from filesystem
admin.actions.sync-db: Synchronize gists from database
admin.actions.git-gc: Garbage collect git repositories
admin.id: ID
admin.user: User
admin.delete: Delete
admin.created_at: Created

admin.config-link: This configuration can be %s by a YAML config file and/or environment variables.
admin.config-link-overriden: overridden
admin.disable-signup: Disable signup
admin.disable-signup_help: Forbid the creation of new accounts.
admin.require-login: Require login
admin.require-login_help: Enforce users to be logged in to see gists.
admin.disable-login: Disable login form
admin.disable-login_help: Forbid logging in via the login form to force using OAuth providers instead.
admin.disable-gravatar: Disable Gravatar
admin.disable-gravatar_help: Disable the usage of Gravatar as an avatar provider.

admin.users.delete_confirm: Do you want to delete this user ?

admin.gists.title: Title
admin.gists.private: Private ?
admin.gists.nb-files: Nb. files
admin.gists.nb-likes: Nb. likes
admin.gists.delete_confirm: Do you want to delete this gist ?
Loading

0 comments on commit a5ea522

Please sign in to comment.