diff --git a/services/userlog/Makefile b/services/userlog/Makefile index ea50ca0ee..79c4acbd3 100644 --- a/services/userlog/Makefile +++ b/services/userlog/Makefile @@ -44,7 +44,7 @@ l10n-push: .PHONY: l10n-read l10n-read: - go-xgettext -o $(OUTPUT_DIR)/locale/default.pot --keyword=Template -s pkg/service/templates.go + go-xgettext -o $(OUTPUT_DIR)/locale/userlog.pot --keyword=Template -s pkg/service/templates.go .PHONY: l10n-write l10n-write: diff --git a/services/userlog/README.md b/services/userlog/README.md index 352e76fa9..2fd9bb618 100644 --- a/services/userlog/README.md +++ b/services/userlog/README.md @@ -31,3 +31,17 @@ The `userlog` service provides an API to retrieve configured events. For now, th ## Deleting To delete events for an user, use a `DELETE` request to `ocs/v2.php/apps/notifications/api/v1/notifications` containing the IDs to delete. + +## Translations + +The `userlog` service uses embedded translations to provide full functionality even in the single binary case. The service supports using custom translations instead. Set `USERLOG_TRANSLATION_PATH` to a folder that contains the translation files. In this folder translation files need to be named `userlog.po` (or `userlog.mo`) and need to be stored in a folder defining their language code. In general the pattern for a translation file needs to be: +``` +{USERLOG_TRANSLATION_PATH}/{language-code}/LC_MESSAGES/userlog.po +``` +So for example for language `en_US` one needs to place the corresponding translation files to `{USERLOG_TRANSLATION_PATH}/en_US/LC_MESSAGES/userlog.po`. + +If a requested translation is not available the service falls back to the language default (so for example if `en_US` is not available, the service would fall back to translations in `en` folder). + +If the default language is also not available (for example the language code is `de_DE` and neither `de_DE` nor `de` folder is available, the service falls back to system default (dev `en`) + +It is currently not possible to mix custom and default translations. diff --git a/services/userlog/pkg/config/config.go b/services/userlog/pkg/config/config.go index 04e74cf3b..085a25042 100644 --- a/services/userlog/pkg/config/config.go +++ b/services/userlog/pkg/config/config.go @@ -22,6 +22,7 @@ type Config struct { MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;USERLOG_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services."` RevaGateway string `yaml:"reva_gateway" env:"REVA_GATEWAY" desc:"CS3 gateway used to look up user metadata"` + TranslationPath string `yaml:"translation_path" env:"USERLOG_TRANSLATION_PATH" desc:"(optional) Set this to a path with custom translations to overwrite buildin translations. See readme for details"` Events Events `yaml:"events"` Store Store `yaml:"store"` diff --git a/services/userlog/pkg/service/conversion.go b/services/userlog/pkg/service/conversion.go index f45f116da..15b6600ef 100644 --- a/services/userlog/pkg/service/conversion.go +++ b/services/userlog/pkg/service/conversion.go @@ -27,9 +27,7 @@ var ( _resourceTypeSpace = "storagespace" _resourceTypeShare = "share" - // TODO: from config - _pathToLocales = "/home/jkoberg/ocis/services/userlog/l10n/locale" - _domain = "default" + _domain = "userlog" ) // OC10Notification is the oc10 style representation of an event @@ -55,6 +53,7 @@ type Converter struct { machineAuthAPIKey string serviceName string registeredEvents map[string]events.Unmarshaller + translationPath string // cached within one request not to query other service too much spaces map[string]*storageprovider.StorageSpace @@ -64,13 +63,14 @@ type Converter struct { } // NewConverter returns a new Converter -func NewConverter(loc string, gwc gateway.GatewayAPIClient, machineAuthAPIKey string, name string, registeredEvents map[string]events.Unmarshaller) *Converter { +func NewConverter(loc string, gwc gateway.GatewayAPIClient, machineAuthAPIKey string, name string, translationPath string, registeredEvents map[string]events.Unmarshaller) *Converter { return &Converter{ locale: loc, gwClient: gwc, machineAuthAPIKey: machineAuthAPIKey, serviceName: name, registeredEvents: registeredEvents, + translationPath: translationPath, spaces: make(map[string]*storageprovider.StorageSpace), users: make(map[string]*user.User), resources: make(map[string]*storageprovider.ResourceInfo), @@ -123,7 +123,7 @@ func (c *Converter) spaceDeletedMessage(eventid string, executant *user.UserId, return OC10Notification{}, err } - subj, subjraw, msg, msgraw, err := composeMessage(SpaceDeleted, c.locale, map[string]interface{}{ + subj, subjraw, msg, msgraw, err := composeMessage(SpaceDeleted, c.locale, c.translationPath, map[string]interface{}{ "username": usr.GetDisplayName(), "spacename": spacename, }) @@ -164,7 +164,7 @@ func (c *Converter) spaceMessage(eventid string, nt NotificationTemplate, execut return OC10Notification{}, err } - subj, subjraw, msg, msgraw, err := composeMessage(nt, c.locale, map[string]interface{}{ + subj, subjraw, msg, msgraw, err := composeMessage(nt, c.locale, c.translationPath, map[string]interface{}{ "username": usr.GetDisplayName(), "spacename": space.GetName(), }) @@ -203,7 +203,7 @@ func (c *Converter) shareMessage(eventid string, nt NotificationTemplate, execut return OC10Notification{}, err } - subj, subjraw, msg, msgraw, err := composeMessage(nt, c.locale, map[string]interface{}{ + subj, subjraw, msg, msgraw, err := composeMessage(nt, c.locale, c.translationPath, map[string]interface{}{ "username": usr.GetDisplayName(), "resourcename": info.GetName(), }) @@ -270,8 +270,8 @@ func (c *Converter) getUser(ctx context.Context, userID *user.UserId) (*user.Use return usr, err } -func composeMessage(nt NotificationTemplate, locale string, vars map[string]interface{}) (string, string, string, string, error) { - subjectraw, messageraw := loadTemplates(nt, locale) +func composeMessage(nt NotificationTemplate, locale string, path string, vars map[string]interface{}) (string, string, string, string, error) { + subjectraw, messageraw := loadTemplates(nt, locale, path) subject, err := executeTemplate(subjectraw, vars) if err != nil { @@ -283,10 +283,15 @@ func composeMessage(nt NotificationTemplate, locale string, vars map[string]inte } -func loadTemplates(nt NotificationTemplate, locale string) (string, string) { +func loadTemplates(nt NotificationTemplate, locale string, path string) (string, string) { // Create Locale with library path and language code and load default domain - l := gotext.NewLocaleFS("l10n/locale", locale, _translationFS) - l.AddDomain(_domain) + var l *gotext.Locale + if path == "" { + l = gotext.NewLocaleFS("l10n/locale", locale, _translationFS) + } else { // use custom path instead + l = gotext.NewLocale(path, locale) + } + l.AddDomain(_domain) // make domain configurable only if needed return l.Get(nt.Subject), l.Get(nt.Message) } diff --git a/services/userlog/pkg/service/http.go b/services/userlog/pkg/service/http.go index ffcbdfaef..7f316cc78 100644 --- a/services/userlog/pkg/service/http.go +++ b/services/userlog/pkg/service/http.go @@ -31,7 +31,7 @@ func (ul *UserlogService) HandleGetEvents(w http.ResponseWriter, r *http.Request return } - conv := NewConverter(r.Header.Get(HeaderPreferedLanguage), ul.gwClient, ul.cfg.MachineAuthAPIKey, ul.cfg.Service.Name, ul.registeredEvents) + conv := NewConverter(r.Header.Get(HeaderPreferedLanguage), ul.gwClient, ul.cfg.MachineAuthAPIKey, ul.cfg.Service.Name, ul.cfg.TranslationPath, ul.registeredEvents) resp := GetEventResponseOC10{} for _, e := range evs { diff --git a/services/userlog/pkg/service/service.go b/services/userlog/pkg/service/service.go index 5fa9c3eef..2c6ee2837 100644 --- a/services/userlog/pkg/service/service.go +++ b/services/userlog/pkg/service/service.go @@ -33,6 +33,7 @@ type UserlogService struct { historyClient ehsvc.EventHistoryService gwClient gateway.GatewayAPIClient registeredEvents map[string]events.Unmarshaller + translationPath string } // NewUserlogService returns an EventHistory service