add documentation and makefile and license WIP Testing Problem

This commit is contained in:
admin 2025-06-24 22:58:18 +02:00
parent a00ea001fb
commit 6e4c5ad6cf
4 changed files with 303 additions and 55 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
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 Normal file
View File

@ -0,0 +1,107 @@
# 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", "€")
```

2
makefile Normal file
View File

@ -0,0 +1,2 @@
test:
- go test .\...

View File

@ -1,72 +1,190 @@
package translation_test
import (
"os"
"testing"
"git.apihub24.de/admin/translation"
exampletranslations "git.apihub24.de/admin/translation/example_translations"
)
func Test_Translation_Init(t *testing.T) {
translation.Init(exampletranslations.Files)
if translation.Get("KEY", "de") != "WERT" {
t.Errorf("translations not initialized!")
const (
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 Test_Translation_Get_de(t *testing.T) {
func TestMain(m *testing.M) {
translation.Init(exampletranslations.Files)
value := translation.Get("KEY", "de")
if value != "WERT" {
t.Errorf("expect German KEY to have Value 'WERT' but was %s", value)
}
code := m.Run()
os.Exit(code)
}
func Test_Translation_Get_en(t *testing.T) {
translation.Init(exampletranslations.Files)
value := translation.Get("KEY", "en")
if value != "VALUE" {
t.Errorf("expect English KEY to have Value 'VALUE' but was %s", value)
func Test(t *testing.T) {
for _, tc := range getTestCases() {
t.Run(tc.name, func(t *testing.T) {
if len(tc.fallbackCulture) > 0 {
translation.SetDefaultCulture(tc.fallbackCulture)
}
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]...)
}
if len(tc.fallbackCulture) > 0 {
translation.SetDefaultCulture("en")
}
if value != tc.expectedValues[idx] {
params := make([]any, 0)
for _, key := range tc.errorMessages[idx].params {
if key == paramTranslationValue {
params = append(params, value)
continue
}
params = append(params, key)
}
t.Errorf(tc.errorMessages[idx].format, params...)
return
}
}
func Test_Translation_Get_fr(t *testing.T) {
translation.Init(exampletranslations.Files)
value := translation.Get("KEY", "fr")
if value != "VALEUR" {
t.Errorf("expect France KEY to have Value 'VALEUR' but was %s", value)
}
}
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)
})
}
}