1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-19 05:09:42 +02:00

i18n server-side strings

This commit is contained in:
Harvey Kandola 2022-03-16 16:58:42 -04:00
parent f4a1350a41
commit df534f72fa
11 changed files with 59 additions and 131 deletions

View file

@ -1,110 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
package database
// import (
// "crypto/rand"
// "time"
// "github.com/documize/community/core/env"
// "github.com/jmoiron/sqlx"
// )
// // Lock will try to lock the database instance to the running process.
// // Uses a "random" delay as a por man's database cluster-aware process.
// // We skip delay if there are no scripts to process.
// func Lock(runtime *env.Runtime, scriptsToProcess int) (bool, error) {
// // Wait for random period of time.
// b := make([]byte, 2)
// _, err := rand.Read(b)
// if err != nil {
// return false, err
// }
// wait := ((time.Duration(b[0]) << 8) | time.Duration(b[1])) * time.Millisecond / 10 // up to 6.5 secs wait
// // Why delay if nothing to process?
// if scriptsToProcess > 0 {
// time.Sleep(wait)
// }
// // Start transaction fotr lock process.
// tx, err := runtime.Db.Beginx()
// if err != nil {
// runtime.Log.Error("Database: unable to start transaction", err)
// return false, err
// }
// // Lock the database.
// _, err = tx.Exec(runtime.StoreProvider.QueryStartLock())
// if err != nil {
// runtime.Log.Error("Database: unable to lock tables", err)
// return false, err
// }
// // Unlock the database at the end of this function.
// defer func() {
// _, err = tx.Exec(runtime.StoreProvider.QueryFinishLock())
// if err != nil {
// runtime.Log.Error("Database: unable to unlock tables", err)
// }
// tx.Commit()
// }()
// // Try to record this process as leader of database migration process.
// _, err = tx.Exec(runtime.StoreProvider.QueryInsertProcessID())
// if err != nil {
// runtime.Log.Info("Database: marked as slave process awaiting upgrade")
// return false, nil
// }
// // We are the leader!
// runtime.Log.Info("Database: marked as database upgrade process leader")
// return true, err
// }
// // Unlock completes process that was started with Lock().
// func Unlock(runtime *env.Runtime, tx *sqlx.Tx, err error, amLeader bool) error {
// if amLeader {
// defer func() {
// doUnlock(runtime)
// }()
// if tx != nil {
// if err == nil {
// tx.Commit()
// runtime.Log.Info("Database: is ready")
// return nil
// }
// tx.Rollback()
// }
// runtime.Log.Error("Database: install/upgrade failed", err)
// return err
// }
// return nil // not the leader, so ignore errors
// }
// // Helper method for defer function called from Unlock().
// func doUnlock(runtime *env.Runtime) error {
// tx, err := runtime.Db.Beginx()
// if err != nil {
// return err
// }
// _, err = tx.Exec(runtime.StoreProvider.QueryDeleteProcessID())
// if err != nil {
// return err
// }
// return tx.Commit()
// }

View file

@ -110,7 +110,7 @@
</div> </div>
<div class="text"> <div class="text">
<h1>Database Error</h1> <h1>Database Error</h1>
<p>There seems to be a problem with the Documize database: <strong>{{.DBname}}</strong></p> <p>There seems to be a problem with the Documize Community database: <strong>{{.DBname}}</strong></p>
<p><em>{{.Issue}}</em></p> <p><em>{{.Issue}}</em></p>
</div> </div>
</div> </div>

View file

@ -4,12 +4,17 @@ import (
"embed" "embed"
"encoding/json" "encoding/json"
"fmt" "fmt"
"strings"
"github.com/documize/community/core/asset" "github.com/documize/community/core/asset"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
const (
DefaultLocale = "en-US"
)
var localeMap map[string]map[string]string var localeMap map[string]map[string]string
// type translation struct { // type translation struct {
@ -57,11 +62,15 @@ func Initialize(e embed.FS) (err error) {
// Localize will returns string value for given key using specified locale). // Localize will returns string value for given key using specified locale).
// e.g. locale = "en-US", key = "admin_billing" // e.g. locale = "en-US", key = "admin_billing"
func Localize(locale, key string) (s string) { //
// Replacements are for replacing string placeholders ({1} {2} {3}) with
// replacement text.
// e.g. "This is {1} example" where replacements[0] will replace {1}
func Localize(locale string, key string, replacements ...string) (s string) {
l, ok := localeMap[locale] l, ok := localeMap[locale]
if !ok { if !ok {
// fallback // fallback
l = localeMap["en-US"] l = localeMap[DefaultLocale]
} }
s, ok = l[key] s, ok = l[key]
@ -70,5 +79,13 @@ func Localize(locale, key string) (s string) {
s = fmt.Sprintf("!! %s !!", key) s = fmt.Sprintf("!! %s !!", key)
} }
// placeholders are one-based: {1} {2} {3}
// replacements array is zero-based hence the +1 below
if len(replacements) > 0 {
for i := range replacements {
s = strings.Replace(s, fmt.Sprintf("{%d}", i+1), replacements[i], 1)
}
}
return return
} }

View file

@ -38,7 +38,7 @@ func CommandWithTimeout(command *exec.Cmd, timeout time.Duration) ([]byte, error
select { select {
case <-time.After(timeout): case <-time.After(timeout):
if err := command.Process.Kill(); err != nil { if err := command.Process.Kill(); err != nil {
fmt.Errorf("failed to kill: ", err) fmt.Printf("failed to kill: %s", err.Error())
} }
<-done // prevent memory leak <-done // prevent memory leak
//fmt.Println("DEBUG timeout") //fmt.Println("DEBUG timeout")

View file

@ -21,6 +21,7 @@ import (
"strings" "strings"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/i18n"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
@ -57,7 +58,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Org contains raw auth provider config // Org contains raw auth provider config
org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID) org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID)
if err != nil { if err != nil {
result.Message = "Error: unable to get organization record" result.Message = i18n.Localize(ctx.Locale, "server_err_org")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -66,7 +67,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Exit if not using Keycloak // Exit if not using Keycloak
if org.AuthProvider != ath.AuthProviderKeycloak { if org.AuthProvider != ath.AuthProviderKeycloak {
result.Message = "Error: skipping user sync with Keycloak as it is not the configured option" result.Message = i18n.Localize(ctx.Locale, "server_keycloak_error1")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Info(result.Message) h.Runtime.Log.Info(result.Message)
@ -77,7 +78,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
c := ath.KeycloakConfig{} c := ath.KeycloakConfig{}
err = json.Unmarshal([]byte(org.AuthConfig), &c) err = json.Unmarshal([]byte(org.AuthConfig), &c)
if err != nil { if err != nil {
result.Message = "Error: unable read Keycloak configuration data" result.Message = i18n.Localize(ctx.Locale, "server_keycloak_error2")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -87,7 +88,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// User list from Keycloak // User list from Keycloak
kcUsers, err := Fetch(c) kcUsers, err := Fetch(c)
if err != nil { if err != nil {
result.Message = "Error: unable to fetch Keycloak users: " + err.Error() result.Message = i18n.Localize(ctx.Locale, "server_keycloak_error3", err.Error())
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -97,7 +98,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// User list from Documize // User list from Documize
dmzUsers, err := h.Store.User.GetUsersForOrganization(ctx, "", 99999) dmzUsers, err := h.Store.User.GetUsersForOrganization(ctx, "", 99999)
if err != nil { if err != nil {
result.Message = "Error: unable to fetch Documize users" result.Message = i18n.Localize(ctx.Locale, "server_error_user")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -135,8 +136,8 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
} }
} }
result.Message = fmt.Sprintf("Keycloak sync found %d users, %d new users added, %d users with missing data ignored", result.Message = i18n.Localize(ctx.Locale, "server_keycloak_summary",
len(kcUsers), len(insert), missing) fmt.Sprintf("%d", len(kcUsers)), fmt.Sprintf("%d", len(insert)), fmt.Sprintf("%d", missing))
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Info(result.Message) h.Runtime.Log.Info(result.Message)

View file

@ -21,6 +21,7 @@ import (
"strings" "strings"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/i18n"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
@ -146,7 +147,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Org contains raw auth provider config // Org contains raw auth provider config
org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID) org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID)
if err != nil { if err != nil {
result.Message = "Error: unable to get organization record" result.Message = i18n.Localize(ctx.Locale, "server_error_org")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -155,7 +156,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Exit if not using LDAP // Exit if not using LDAP
if org.AuthProvider != ath.AuthProviderLDAP { if org.AuthProvider != ath.AuthProviderLDAP {
result.Message = "Error: skipping user sync with LDAP as it is not the configured option" result.Message = i18n.Localize(ctx.Locale, "server_ldap_error1")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Info(result.Message) h.Runtime.Log.Info(result.Message)
@ -166,7 +167,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
c := lm.LDAPConfig{} c := lm.LDAPConfig{}
err = json.Unmarshal([]byte(org.AuthConfig), &c) err = json.Unmarshal([]byte(org.AuthConfig), &c)
if err != nil { if err != nil {
result.Message = "Error: unable read LDAP configuration data" result.Message = i18n.Localize(ctx.Locale, "server_ldap_error2")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -176,7 +177,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Get user list from LDAP. // Get user list from LDAP.
ldapUsers, err := fetchUsers(c) ldapUsers, err := fetchUsers(c)
if err != nil { if err != nil {
result.Message = "Error: unable to fetch LDAP users: " + err.Error() result.Message = i18n.Localize(ctx.Locale, "server_ldap_error3", err.Error())
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -186,7 +187,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Get user list from Documize // Get user list from Documize
dmzUsers, err := h.Store.User.GetUsersForOrganization(ctx, "", 99999) dmzUsers, err := h.Store.User.GetUsersForOrganization(ctx, "", 99999)
if err != nil { if err != nil {
result.Message = "Error: unable to fetch Documize users" result.Message = i18n.Localize(ctx.Locale, "server_error_user")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -223,10 +224,8 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
} }
result.IsError = false result.IsError = false
result.Message = "Sync complete with LDAP server" result.Message = i18n.Localize(ctx.Locale, "server_ldap_complete")
result.Message = fmt.Sprintf( result.Message = i18n.Localize(ctx.Locale, "server_ldap_summary", fmt.Sprintf("%d", len(ldapUsers)), fmt.Sprintf("%d", len(insert)), fmt.Sprintf("%d", missing))
"LDAP sync found %d users, %d new users added, %d users with missing data ignored",
len(ldapUsers), len(insert), missing)
h.Runtime.Log.Info(result.Message) h.Runtime.Log.Info(result.Message)

View file

@ -44,6 +44,7 @@ type RequestContext struct {
GlobalAdmin bool GlobalAdmin bool
ViewUsers bool ViewUsers bool
Subscription Subscription Subscription Subscription
Locale string
} }
//GetAppURL returns full HTTP url for the app //GetAppURL returns full HTTP url for the app

View file

@ -689,5 +689,19 @@
"404": "Oops! That page couldn't be found.", "404": "Oops! That page couldn't be found.",
"404_explain": "Maybe the content you're looking for is no longer available?", "404_explain": "Maybe the content you're looking for is no longer available?",
"close_account": "Please close my Documize account.", "close_account": "Please close my Documize account.",
"third_party": "Documize Community utilizes open source libraries and components from third parties" "third_party": "Documize Community utilizes open source libraries and components from third parties",
"server_ldap_error1": "Error: skipping user sync with LDAP as it is not the configured option",
"server_ldap_error2": "Error: unable read LDAP configuration data",
"server_ldap_error3": "Error: unable to fetch LDAP users: {1}",
"server_ldap_complete": "Sync complete with LDAP server",
"server_ldap_summary": "LDAP sync found {1} users, {2} new users added, {3} users with missing data ignored",
"server_keycloak_error1": "Error: skipping user sync with Keycloak as it is not the configured option",
"server_keycloak_error2": "Error: unable read Keycloak configuration data",
"server_keycloak_error3": "Error: unable to fetch Keycloak users: {1}",
"server_keycloak_summary": "Keycloak sync found {1} users, {2} new users added, {3} users with missing data ignored",
"server_error_user": "Error: unable to fetch users",
"server_error_org": "Error: unable to get organization record"
} }

View file

@ -37,6 +37,7 @@ type User struct {
Reset string `json:"-"` Reset string `json:"-"`
LastVersion string `json:"lastVersion"` LastVersion string `json:"lastVersion"`
Theme string `json:"theme"` Theme string `json:"theme"`
Locale string `json:"locale"`
Accounts []account.Account `json:"accounts"` Accounts []account.Account `json:"accounts"`
Groups []group.Record `json:"groups"` Groups []group.Record `json:"groups"`
} }

View file

@ -22,6 +22,7 @@ import (
"time" "time"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/i18n"
"github.com/documize/community/core/request" "github.com/documize/community/core/request"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/domain" "github.com/documize/community/domain"
@ -191,6 +192,10 @@ func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http
rc.GlobalAdmin = u.GlobalAdmin rc.GlobalAdmin = u.GlobalAdmin
rc.ViewUsers = u.ViewUsers rc.ViewUsers = u.ViewUsers
rc.Fullname = u.Fullname() rc.Fullname = u.Fullname()
rc.Locale = u.Locale
if len(rc.Locale) == 0 {
u.Locale = i18n.DefaultLocale
}
// We send back with every HTTP request/response cycle the latest // We send back with every HTTP request/response cycle the latest
// user state. This helps client-side applications to detect changes in // user state. This helps client-side applications to detect changes in