Compare commits
No commits in common. "main" and "v1.0.0" have entirely different histories.
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,2 +1 @@
|
|||||||
.vscode
|
.vscode
|
||||||
.idea
|
|
||||||
21
LICENSE
21
LICENSE
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2025 Markus Morgenstern
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
107
README.md
107
README.md
@ -1,107 +0,0 @@
|
|||||||
# Translation
|
|
||||||
|
|
||||||
A package that handles translations using JSON files.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
To include the translation package in your Go project, run the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get git.apihub24.de/admin/translation
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
You must have a folder containing JSON files. Each file should hold an object with key-value pairs where the value is a string.
|
|
||||||
|
|
||||||
- translations/de.json
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"SOME_KEY": "ein Wert"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- translations/en.json
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"SOME_KEY": "a Value"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Next, you can create a Go file that embeds these JSON files using fs.Files.
|
|
||||||
|
|
||||||
- translations/files.go
|
|
||||||
|
|
||||||
```go
|
|
||||||
package translations
|
|
||||||
|
|
||||||
import "embed"
|
|
||||||
|
|
||||||
//go:embed *.json
|
|
||||||
var Files embed.FS
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, use the translation package to set up and retrieve translations on demand:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.apihub24.de/admin/translation"
|
|
||||||
translations "{link to your translations folder}"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// give the Init Function the translations fs.Files
|
|
||||||
translation.Init(translations.Files)
|
|
||||||
// Optional you can change the Fallback Language
|
|
||||||
translation.SetDefaultCulture("de")
|
|
||||||
|
|
||||||
// get the Translation Values
|
|
||||||
translation.Get("SOME_KEY", "de")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Error Handling & Fallback Behavior
|
|
||||||
|
|
||||||
- If a translation key is not found for the requested language, the package will attempt to retrieve the translation from the default culture (set via SetDefaultCulture).
|
|
||||||
- If the key is not found even in the default culture, or if the specified language file does not exist, translation.Get will return the following string 'no value for key {key} found in source {culture}'. This helps in identifying missing translations directly in your application.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
### Translation Keys
|
|
||||||
|
|
||||||
You can use any translation key you want. The only thing to keep in mind is that your translation files must have the translation key as part of their filename.
|
|
||||||
|
|
||||||
For example, if you want to split your translation files into modules, you can name them like this:
|
|
||||||
|
|
||||||
- translations/global_de.json
|
|
||||||
- translations/global_en.json
|
|
||||||
- translations/mod1_de.json
|
|
||||||
- translations/mod1_en.json
|
|
||||||
- translations/mod2_de.json
|
|
||||||
- translations/mod2_en.json
|
|
||||||
|
|
||||||
And the translation call would look like this:
|
|
||||||
|
|
||||||
```go
|
|
||||||
translation.Get("SOME_KEY", "global_de")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Parameters
|
|
||||||
|
|
||||||
You can use placeholders to create dynamic translation values.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"DYNAMIC": "Preis: %[1]s %[1]s"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
|
||||||
translation.Get("DYNAMIC", "de", "12,50", "€")
|
|
||||||
```
|
|
||||||
@ -1,190 +1,72 @@
|
|||||||
package translation_test
|
package translation_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.apihub24.de/admin/translation"
|
"git.apihub24.de/admin/translation"
|
||||||
exampletranslations "git.apihub24.de/admin/translation/example_translations"
|
exampletranslations "git.apihub24.de/admin/translation/example_translations"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
func Test_Translation_Init(t *testing.T) {
|
||||||
paramTranslationValue = "tlv"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testCaseError struct {
|
|
||||||
format string
|
|
||||||
params []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type testCase struct {
|
|
||||||
name string
|
|
||||||
fallbackCulture string
|
|
||||||
keys []string
|
|
||||||
keyParams [][]string
|
|
||||||
cultures []string
|
|
||||||
expectedValues []string
|
|
||||||
errorMessages []testCaseError
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTestCases() []testCase {
|
|
||||||
return []testCase{
|
|
||||||
{
|
|
||||||
name: "Test Translation.Init",
|
|
||||||
fallbackCulture: "",
|
|
||||||
keys: []string{"KEY"},
|
|
||||||
keyParams: [][]string{},
|
|
||||||
cultures: []string{"de"},
|
|
||||||
expectedValues: []string{"WERT"},
|
|
||||||
errorMessages: []testCaseError{
|
|
||||||
{
|
|
||||||
format: "translations not initialized!",
|
|
||||||
params: []string{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test Translation.Get de",
|
|
||||||
fallbackCulture: "",
|
|
||||||
keys: []string{"KEY"},
|
|
||||||
keyParams: [][]string{},
|
|
||||||
cultures: []string{"de"},
|
|
||||||
expectedValues: []string{"WERT"},
|
|
||||||
errorMessages: []testCaseError{
|
|
||||||
{
|
|
||||||
format: "expect German KEY to have Value 'WERT' but was %[1]s",
|
|
||||||
params: []string{paramTranslationValue},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test Translation.Get en",
|
|
||||||
fallbackCulture: "",
|
|
||||||
keys: []string{"KEY"},
|
|
||||||
keyParams: [][]string{},
|
|
||||||
cultures: []string{"en"},
|
|
||||||
expectedValues: []string{"VALUE"},
|
|
||||||
errorMessages: []testCaseError{
|
|
||||||
{
|
|
||||||
format: "expect English KEY to have Value 'VALUE' but was %[1]s",
|
|
||||||
params: []string{paramTranslationValue},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test Translation.Get fr",
|
|
||||||
fallbackCulture: "",
|
|
||||||
keys: []string{"KEY"},
|
|
||||||
keyParams: [][]string{},
|
|
||||||
cultures: []string{"fr"},
|
|
||||||
expectedValues: []string{"VALEUR"},
|
|
||||||
errorMessages: []testCaseError{
|
|
||||||
{
|
|
||||||
format: "expect France KEY to have Value 'VALEUR' but was %[1]s",
|
|
||||||
params: []string{paramTranslationValue},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test Translation.Get missing Culture Fallback to English",
|
|
||||||
fallbackCulture: "",
|
|
||||||
keys: []string{"KEY"},
|
|
||||||
keyParams: [][]string{},
|
|
||||||
cultures: []string{"notexists"},
|
|
||||||
expectedValues: []string{"VALUE"},
|
|
||||||
errorMessages: []testCaseError{
|
|
||||||
{
|
|
||||||
format: "expect Fallback KEY to have Value 'VALUE' but was %[1]s",
|
|
||||||
params: []string{paramTranslationValue},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test Translation.Get can change Fallback to German",
|
|
||||||
fallbackCulture: "de",
|
|
||||||
keys: []string{"KEY"},
|
|
||||||
keyParams: [][]string{},
|
|
||||||
cultures: []string{"notexists"},
|
|
||||||
expectedValues: []string{"WERT"},
|
|
||||||
errorMessages: []testCaseError{
|
|
||||||
{
|
|
||||||
format: "expect Fallback KEY to have Value 'WERT' but was %[1]s",
|
|
||||||
params: []string{paramTranslationValue},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test Translation.Get return Error message when Key not exists",
|
|
||||||
fallbackCulture: "",
|
|
||||||
keys: []string{"notexists"},
|
|
||||||
keyParams: [][]string{},
|
|
||||||
cultures: []string{"de"},
|
|
||||||
expectedValues: []string{"no value for key notexists found in source de"},
|
|
||||||
errorMessages: []testCaseError{
|
|
||||||
{
|
|
||||||
format: "expect to get the missing key and culture but get: %[1]s",
|
|
||||||
params: []string{paramTranslationValue},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Test Translation.Get with Parameters",
|
|
||||||
fallbackCulture: "",
|
|
||||||
keys: []string{"WITH_PARAM", "WITH_PARAM", "WITH_PARAM"},
|
|
||||||
keyParams: [][]string{{"a", "b"}, {"a", "b"}, {"a", "b"}},
|
|
||||||
cultures: []string{"de", "en", "fr"},
|
|
||||||
expectedValues: []string{"WERT a_b", "VALUE a_b", "VALEUR a_b"},
|
|
||||||
errorMessages: []testCaseError{
|
|
||||||
{
|
|
||||||
format: "expect German KEY to have Value 'WERT a_b' but was %[1]s",
|
|
||||||
params: []string{paramTranslationValue},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
format: "expect English KEY to have Value 'VALUE a_b' but was %[1]s",
|
|
||||||
params: []string{paramTranslationValue},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
format: "expect France KEY to have Value 'VALEUR a_b' but was %[1]s",
|
|
||||||
params: []string{paramTranslationValue},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
|
||||||
translation.Init(exampletranslations.Files)
|
translation.Init(exampletranslations.Files)
|
||||||
code := m.Run()
|
if translation.Get("KEY", "de") != "WERT" {
|
||||||
os.Exit(code)
|
t.Errorf("translations not initialized!")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
func Test(t *testing.T) {
|
|
||||||
for _, tc := range getTestCases() {
|
func Test_Translation_Get_de(t *testing.T) {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
translation.Init(exampletranslations.Files)
|
||||||
if len(tc.fallbackCulture) > 0 {
|
value := translation.Get("KEY", "de")
|
||||||
translation.SetDefaultCulture(tc.fallbackCulture)
|
if value != "WERT" {
|
||||||
}
|
t.Errorf("expect German KEY to have Value 'WERT' but was %s", value)
|
||||||
for idx := range tc.keys {
|
}
|
||||||
value := translation.Get(tc.keys[idx], tc.cultures[idx])
|
}
|
||||||
if idx < len(tc.keyParams) {
|
|
||||||
value = translation.Get(tc.keys[idx], tc.cultures[idx], tc.keyParams[idx]...)
|
func Test_Translation_Get_en(t *testing.T) {
|
||||||
}
|
translation.Init(exampletranslations.Files)
|
||||||
if len(tc.fallbackCulture) > 0 {
|
value := translation.Get("KEY", "en")
|
||||||
translation.SetDefaultCulture("en")
|
if value != "VALUE" {
|
||||||
}
|
t.Errorf("expect English KEY to have Value 'VALUE' but was %s", value)
|
||||||
if value != tc.expectedValues[idx] {
|
}
|
||||||
params := make([]any, 0)
|
}
|
||||||
for _, key := range tc.errorMessages[idx].params {
|
|
||||||
if key == paramTranslationValue {
|
func Test_Translation_Get_fr(t *testing.T) {
|
||||||
params = append(params, value)
|
translation.Init(exampletranslations.Files)
|
||||||
continue
|
value := translation.Get("KEY", "fr")
|
||||||
}
|
if value != "VALEUR" {
|
||||||
params = append(params, key)
|
t.Errorf("expect France KEY to have Value 'VALEUR' but was %s", value)
|
||||||
}
|
}
|
||||||
t.Errorf(tc.errorMessages[idx].format, params...)
|
}
|
||||||
return
|
|
||||||
}
|
func Test_Translation_Get_Fallback_en(t *testing.T) {
|
||||||
}
|
translation.Init(exampletranslations.Files)
|
||||||
})
|
value := translation.Get("KEY", "notexists")
|
||||||
|
if value != "VALUE" {
|
||||||
|
t.Errorf("expect Fallback KEY to have Value 'VALUE' but was %s", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Translation_Can_Change_Fallback_Language(t *testing.T) {
|
||||||
|
translation.Init(exampletranslations.Files)
|
||||||
|
translation.SetDefaultCulture("de")
|
||||||
|
value := translation.Get("KEY", "notexists")
|
||||||
|
if value != "WERT" {
|
||||||
|
t.Errorf("expect Fallback KEY to have Value 'WERT' but was %s", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_Translation_Get_With_Parameter(t *testing.T) {
|
||||||
|
translation.Init(exampletranslations.Files)
|
||||||
|
valueDe := translation.Get("WITH_PARAM", "de", "a", "b")
|
||||||
|
valueEn := translation.Get("WITH_PARAM", "en", "a", "b")
|
||||||
|
valueFr := translation.Get("WITH_PARAM", "fr", "a", "b")
|
||||||
|
if valueDe != "WERT a_b" {
|
||||||
|
t.Errorf("expect German KEY to have Value 'WERT a_b' but was %s", valueDe)
|
||||||
|
}
|
||||||
|
if valueEn != "VALUE a_b" {
|
||||||
|
t.Errorf("expect English KEY to have Value 'VALUE a_b' but was %s", valueEn)
|
||||||
|
}
|
||||||
|
if valueFr != "VALEUR a_b" {
|
||||||
|
t.Errorf("expect France KEY to have Value 'VALEUR a_b' but was %s", valueFr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
21
v2/LICENSE
21
v2/LICENSE
@ -1,21 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2025 Markus Morgenstern
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
110
v2/README.md
110
v2/README.md
@ -1,110 +0,0 @@
|
|||||||
# Translation
|
|
||||||
|
|
||||||
A package that handles translations using JSON files.
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
To include the translation package in your Go project, run the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go get git.apihub24.de/admin/translation
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
You must have a folder containing JSON files. Each file should hold an object with key-value pairs where the value is a string.
|
|
||||||
|
|
||||||
- translations/de.json
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"SOME_KEY": "ein Wert"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- translations/en.json
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"SOME_KEY": "a Value"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Next, you can create a Go file that embeds these JSON files using embed.FS.
|
|
||||||
|
|
||||||
- translations/files.go
|
|
||||||
|
|
||||||
```go
|
|
||||||
package translations
|
|
||||||
|
|
||||||
import "embed"
|
|
||||||
|
|
||||||
//go:embed *.json
|
|
||||||
var Files embed.FS
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, use the translation package to set up and retrieve translations on demand:
|
|
||||||
|
|
||||||
```go
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
di "git.apihub24.de/admin/generic-di"
|
|
||||||
"git.apihub24.de/admin/translation"
|
|
||||||
translations "{link to your translations folder}"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// get the Service from DI
|
|
||||||
translationService := di.Inject[translation.ITranslationService]()
|
|
||||||
// give the Init Function the translations fs.Files
|
|
||||||
translationService.Init(translations.Files)
|
|
||||||
// Optional you can change the Fallback Language
|
|
||||||
translationService.SetDefaultCulture("de")
|
|
||||||
|
|
||||||
// get the Translation Values
|
|
||||||
translationService.Get("SOME_KEY", "de")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Error Handling & Fallback Behavior
|
|
||||||
|
|
||||||
- If a translation key is not found for the requested language, the package will attempt to retrieve the translation from the default culture (set via SetDefaultCulture).
|
|
||||||
- If the key is not found even in the default culture, or if the specified language file does not exist, translation.Get will return the following string 'no value for key {key} found in source {culture}'. This helps in identifying missing translations directly in your application.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
### Translation Keys
|
|
||||||
|
|
||||||
You can use any translation key you want. The only thing to keep in mind is that your translation files must have the translation key as part of their filename.
|
|
||||||
|
|
||||||
For example, if you want to split your translation files into modules, you can name them like this:
|
|
||||||
|
|
||||||
- translations/global_de.json
|
|
||||||
- translations/global_en.json
|
|
||||||
- translations/mod1_de.json
|
|
||||||
- translations/mod1_en.json
|
|
||||||
- translations/mod2_de.json
|
|
||||||
- translations/mod2_en.json
|
|
||||||
|
|
||||||
And the translation call would look like this:
|
|
||||||
|
|
||||||
```go
|
|
||||||
translationService.Get("SOME_KEY", "global_de")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Parameters
|
|
||||||
|
|
||||||
You can use placeholders to create dynamic translation values.
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"DYNAMIC": "Preis: %[1]s %[1]s"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
|
||||||
translationService.Get("DYNAMIC", "de", "12,50", "€")
|
|
||||||
```
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"KEY": "WERT",
|
|
||||||
"WITH_PARAM": "WERT %[1]s_%[2]s"
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"KEY": "VALUE",
|
|
||||||
"WITH_PARAM": "VALUE %[1]s_%[2]s"
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
package exampletranslations
|
|
||||||
|
|
||||||
import "embed"
|
|
||||||
|
|
||||||
//go:embed *.json
|
|
||||||
var Files embed.FS
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"KEY": "VALEUR",
|
|
||||||
"WITH_PARAM": "VALEUR %[1]s_%[2]s"
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
module git.apihub24.de/admin/translation/v2
|
|
||||||
|
|
||||||
go 1.22.0
|
|
||||||
|
|
||||||
require git.apihub24.de/admin/generic-di v1.4.0
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
git.apihub24.de/admin/generic-di v1.4.0 h1:0mQnpAcavMLBcnF5UO+tUI7abZ6zQPleqPsjEk3WIaU=
|
|
||||||
git.apihub24.de/admin/generic-di v1.4.0/go.mod h1:VcHV8MOb1qhwabHdO09CpjEg2VaDesehul86g1iyOxY=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
test:
|
|
||||||
- go test .\...
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
package translation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"embed"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
di "git.apihub24.de/admin/generic-di"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
di.Injectable(newTranslationService)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ITranslationService interface {
|
|
||||||
Init(files embed.FS)
|
|
||||||
SetDefaultCulture(culture string)
|
|
||||||
Get(key string, culture string, args ...string) string
|
|
||||||
GetSource(culture string) map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
type translationService struct {
|
|
||||||
defaultCulture string
|
|
||||||
translationFiles embed.FS
|
|
||||||
sources map[string]map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTranslationService() ITranslationService {
|
|
||||||
return &translationService{
|
|
||||||
defaultCulture: "en",
|
|
||||||
translationFiles: embed.FS{},
|
|
||||||
sources: make(map[string]map[string]string),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (translation *translationService) Init(files embed.FS) {
|
|
||||||
translation.translationFiles = files
|
|
||||||
}
|
|
||||||
|
|
||||||
func (translation *translationService) SetDefaultCulture(culture string) {
|
|
||||||
translation.defaultCulture = strings.ToLower(culture)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (translation *translationService) Get(key string, culture string, args ...string) string {
|
|
||||||
source, err := translation.loadSource(culture)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Sprintf("unknown error: %s", err.Error())
|
|
||||||
}
|
|
||||||
value, ok := source[key]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Sprintf("no value for key %s found in source %s", key, culture)
|
|
||||||
}
|
|
||||||
if len(args) > 0 {
|
|
||||||
var tmpArgs = make([]any, len(args))
|
|
||||||
for idx, arg := range args {
|
|
||||||
tmpArgs[idx] = arg
|
|
||||||
}
|
|
||||||
return fmt.Sprintf(value, tmpArgs...)
|
|
||||||
}
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (translation *translationService) GetSource(culture string) map[string]string {
|
|
||||||
source, err := translation.loadSource(culture)
|
|
||||||
if err != nil {
|
|
||||||
return make(map[string]string)
|
|
||||||
}
|
|
||||||
return source
|
|
||||||
}
|
|
||||||
|
|
||||||
func (translation *translationService) loadSource(culture string) (map[string]string, error) {
|
|
||||||
source, ok := translation.sources[culture]
|
|
||||||
if ok {
|
|
||||||
return source, nil
|
|
||||||
}
|
|
||||||
data, err := translation.translationFiles.ReadFile(fmt.Sprintf("%s.json", strings.ToLower(culture)))
|
|
||||||
if err != nil {
|
|
||||||
data, err = translation.translationFiles.ReadFile(fmt.Sprintf("%s.json", translation.defaultCulture))
|
|
||||||
if err != nil {
|
|
||||||
return source, fmt.Errorf("can not load translation source for culture %s DefaultCulture: %s", culture, translation.defaultCulture)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = json.Unmarshal(data, &source)
|
|
||||||
if err != nil {
|
|
||||||
return source, fmt.Errorf("can not parse translation source %s", culture)
|
|
||||||
}
|
|
||||||
translation.sources[culture] = source
|
|
||||||
return translation.sources[culture], nil
|
|
||||||
}
|
|
||||||
@ -1,108 +0,0 @@
|
|||||||
package translation_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
di "git.apihub24.de/admin/generic-di"
|
|
||||||
"git.apihub24.de/admin/translation/v2"
|
|
||||||
exampletranslations "git.apihub24.de/admin/translation/v2/example_translations"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTranslationService_Init_And_Get_de(t *testing.T) {
|
|
||||||
translationService := di.Inject[translation.ITranslationService]()
|
|
||||||
translationService.Init(exampletranslations.Files)
|
|
||||||
value := translationService.Get("KEY", "de")
|
|
||||||
if value != "WERT" {
|
|
||||||
t.Errorf("expect 'WERT' for key 'KEY' but was %s", value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTranslationService_Get_de_en_fr(t *testing.T) {
|
|
||||||
translationService := di.Inject[translation.ITranslationService]()
|
|
||||||
translationService.Init(exampletranslations.Files)
|
|
||||||
value := translationService.Get("KEY", "de")
|
|
||||||
if value != "WERT" {
|
|
||||||
t.Errorf("expect 'WERT' for key 'KEY' but was %s", value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value = translationService.Get("KEY", "en")
|
|
||||||
if value != "VALUE" {
|
|
||||||
t.Errorf("expect 'VALUE' for key 'KEY' but was %s", value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value = translationService.Get("KEY", "fr")
|
|
||||||
if value != "VALEUR" {
|
|
||||||
t.Errorf("expect 'VALEUR' for key 'KEY' but was %s", value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTranslationService_Fallback_Is_en(t *testing.T) {
|
|
||||||
translationService := di.Inject[translation.ITranslationService]("TestTranslationService_Fallback_Is_en")
|
|
||||||
translationService.Init(exampletranslations.Files)
|
|
||||||
value := translationService.Get("KEY", "notexists")
|
|
||||||
if value != "VALUE" {
|
|
||||||
t.Errorf("expect 'VALUE' for key 'KEY' as Fallback but was %s", value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTranslationService_SetDefaultCulture_de(t *testing.T) {
|
|
||||||
translationService := di.Inject[translation.ITranslationService]("TestTranslationService_SetDefaultCulture_de")
|
|
||||||
translationService.Init(exampletranslations.Files)
|
|
||||||
translationService.SetDefaultCulture("de")
|
|
||||||
value := translationService.Get("KEY", "notexists")
|
|
||||||
if value != "WERT" {
|
|
||||||
t.Errorf("expect 'WERT' for key 'KEY' as Fallback but was %s", value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTranslationService_Error_Message_On_Key_Not_Exists(t *testing.T) {
|
|
||||||
translationService := di.Inject[translation.ITranslationService]()
|
|
||||||
translationService.Init(exampletranslations.Files)
|
|
||||||
value := translationService.Get("notexists", "de")
|
|
||||||
if value != "no value for key notexists found in source de" {
|
|
||||||
t.Errorf("expect Error Message 'no value for key notexists found in source de' but was %s", value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTranslationService_Get_Key_With_Parameter(t *testing.T) {
|
|
||||||
translationService := di.Inject[translation.ITranslationService]()
|
|
||||||
translationService.Init(exampletranslations.Files)
|
|
||||||
value := translationService.Get("WITH_PARAM", "de", "a", "b")
|
|
||||||
if value != "WERT a_b" {
|
|
||||||
t.Errorf("expect 'WERT a_b' for key 'WITH_PARAM' but was %s", value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value = translationService.Get("WITH_PARAM", "en", "a", "b")
|
|
||||||
if value != "VALUE a_b" {
|
|
||||||
t.Errorf("expect 'VALUE a_b' for key 'WITH_PARAM' but was %s", value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
value = translationService.Get("WITH_PARAM", "fr", "a", "b")
|
|
||||||
if value != "VALEUR a_b" {
|
|
||||||
t.Errorf("expect 'VALEUR a_b' for key 'WITH_PARAM' but was %s", value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTranslationService_GetSource(t *testing.T) {
|
|
||||||
translationService := di.Inject[translation.ITranslationService]()
|
|
||||||
translationService.Init(exampletranslations.Files)
|
|
||||||
data := translationService.GetSource("de")
|
|
||||||
if data == nil {
|
|
||||||
t.Errorf("no map get from source")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v, ok := data["KEY"]; !ok || v != "WERT" {
|
|
||||||
t.Errorf("missing key 'KEY' with value 'WERT'")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if v, ok := data["WITH_PARAM"]; !ok || v != "WERT %[1]s_%[2]s" {
|
|
||||||
t.Errorf("missing key 'WITH_PARAM' with value 'WERT %%[1]s_%%[2]s'")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user