Skip to content

Commit

Permalink
feat: add documentation, expand tests and rename language->locale
Browse files Browse the repository at this point in the history
  • Loading branch information
jonathandavies-CS committed Feb 14, 2025
1 parent 5190a3a commit 4ad823b
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 77 deletions.
38 changes: 38 additions & 0 deletions docs/resources/realm_translation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
page_title: "keycloak_realm_translation Resource"
---

# keycloak_realm_tranlsation Resource

Allows for managing Realm Translations overrides within Keycloak.

A translation defines a schema for representing a locale with a map of key/value pairs and how they are managed within a realm.

Note: whilst you can provide translations for unsupported locales, they will not take effect until they are defined within the realm resource.

## Example Usage

```hcl
resource "keycloak_realm" "realm" {
realm = "my-realm"
}
resource "keycloak_realm_translation" "realm_translation" {
realm_id = keycloak_realm.my_realm.id
locale = "de"
translations = {
"Hello" : "Hallo"
}
}
```

## Argument Reference

- `realm_id` - (Required) The ID of the realm the user profile applies to.
- `locale` - (Required) The locale (language code) the translations apply to.
- `translations` - (Optional) A map of translation keys to values.


## Import

This resource does not currently support importing.
2 changes: 1 addition & 1 deletion example/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ resource "keycloak_realm" "test" {

resource "keycloak_realm_translation" "test_translation" {
realm_id = keycloak_realm.test.id
language = "en"
locale = "en"
translations = {
"test" : "translation"
}
Expand Down
21 changes: 11 additions & 10 deletions keycloak/realm_translation.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import (
"net/http"
)

type RealmLanguageTranslation struct {
Language string `json:"language"`
type RealmLocaleTranslation struct {
Locale string `json:"locale"`
Translations map[string]string `json:"translations"`
}

func (keycloakClient *KeycloakClient) UpdateRealmTranslations(ctx context.Context, realmId string, language string, translations map[string]string) error {
func (keycloakClient *KeycloakClient) UpdateRealmTranslations(ctx context.Context, realmId string, locale string, translations map[string]string) error {
var existingTranslations map[string]string
data, _ := keycloakClient.getRaw(ctx, fmt.Sprintf("/realms/%s/localization/%s", realmId, language), nil)

data, _ := keycloakClient.getRaw(ctx, fmt.Sprintf("/realms/%s/localization/%s", realmId, locale), nil)
err := json.Unmarshal(data, &existingTranslations)
if err != nil {
return nil
Expand All @@ -27,13 +28,13 @@ func (keycloakClient *KeycloakClient) UpdateRealmTranslations(ctx context.Contex
}
}
for _, key := range translationsToDelete {
err := keycloakClient.delete(ctx, fmt.Sprintf("/realms/%s/localization/%s/%s", realmId, language, key), nil)
err := keycloakClient.delete(ctx, fmt.Sprintf("/realms/%s/localization/%s/%s", realmId, locale, key), nil)
if err != nil {
return err
}
}
for key, value := range translations {
err := keycloakClient.putPlain(ctx, fmt.Sprintf("/realms/%s/localization/%s/%s", realmId, language, key), value)
err := keycloakClient.putPlain(ctx, fmt.Sprintf("/realms/%s/localization/%s/%s", realmId, locale, key), value)
if err != nil {
return err
}
Expand All @@ -52,18 +53,18 @@ func (keycloakClient *KeycloakClient) putPlain(ctx context.Context, path string,
return err
}

func (keycloakClient *KeycloakClient) GetRealmTranslations(ctx context.Context, realmId string, language string) (*map[string]string, error) {
func (keycloakClient *KeycloakClient) GetRealmTranslations(ctx context.Context, realmId string, locale string) (*map[string]string, error) {
keyValues := make(map[string]string)
err := keycloakClient.get(ctx, fmt.Sprintf("/realms/%s/localization/%s", realmId, language), &keyValues, nil)
err := keycloakClient.get(ctx, fmt.Sprintf("/realms/%s/localization/%s", realmId, locale), &keyValues, nil)
if err != nil {
return nil, err
}
return &keyValues, nil
}

func (keycloakClient *KeycloakClient) DeleteRealmTranslations(ctx context.Context, realmId string, language string, translations map[string]string) error {
func (keycloakClient *KeycloakClient) DeleteRealmTranslations(ctx context.Context, realmId string, locale string, translations map[string]string) error {
for key := range translations {
err := keycloakClient.delete(ctx, fmt.Sprintf("/realms/%s/localization/%s/%s", realmId, language, key), nil)
err := keycloakClient.delete(ctx, fmt.Sprintf("/realms/%s/localization/%s/%s", realmId, locale, key), nil)
if err != nil {
return err
}
Expand Down
34 changes: 19 additions & 15 deletions provider/resource_keycloak_realm_translation.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,27 @@ func resourceKeycloakRealmTranslation() *schema.Resource {
ReadContext: resourceKeycloakRealmTranslationRead,
DeleteContext: resourceKeycloakRealmTranslationDelete,
UpdateContext: resourceKeycloakRealmTranslationUpdate,
Description: "Manage realm-level translations.",
Schema: map[string]*schema.Schema{
"realm_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The realm in which the translation exists.",
},
"language": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
"locale": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Description: "The locale for the translations.",
},
"translations": {
Optional: true,
Type: schema.TypeMap,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Description: "The mapping of translation keys to values.",
},
},
}
Expand All @@ -40,39 +44,39 @@ func resourceKeycloakRealmTranslation() *schema.Resource {
func resourceKeycloakRealmTranslationRead(ctx context.Context, data *schema.ResourceData, meta interface{}) diag.Diagnostics {
keycloakClient := meta.(*keycloak.KeycloakClient)
realmId := data.Get("realm_id").(string)
language := data.Get("language").(string)
realmLanguageTranslations, err := keycloakClient.GetRealmTranslations(ctx, realmId, language)
locale := data.Get("locale").(string)
realmLocaleTranslations, err := keycloakClient.GetRealmTranslations(ctx, realmId, locale)
if err != nil {
return diag.FromErr(err)
}
data.Set("translations", realmLanguageTranslations)
data.Set("translations", realmLocaleTranslations)
return nil
}

func resourceKeycloakRealmTranslationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*keycloak.KeycloakClient)
realm := d.Get("realm_id").(string)
language := d.Get("language").(string)
locale := d.Get("locale").(string)
translations := d.Get("translations").(map[string]interface{})
translationsConverted := convertTranslations(translations)

err := client.UpdateRealmTranslations(ctx, realm, language, translationsConverted)
err := client.UpdateRealmTranslations(ctx, realm, locale, translationsConverted)
if err != nil {
return diag.FromErr(err)
}

d.SetId(fmt.Sprintf("%s/%s", realm, language)) // Set resource ID as "realm/language"
d.SetId(fmt.Sprintf("%s/%s", realm, locale)) // Set resource ID as "realm/locale"
return resourceKeycloakRealmTranslationRead(ctx, d, meta)
}

func resourceKeycloakRealmTranslationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*keycloak.KeycloakClient)
realm := d.Get("realm_id").(string)
language := d.Get("language").(string)
locale := d.Get("locale").(string)
translations := d.Get("translations").(map[string]interface{})
translationsConverted := convertTranslations(translations)

err := client.DeleteRealmTranslations(ctx, realm, language, translationsConverted)
err := client.DeleteRealmTranslations(ctx, realm, locale, translationsConverted)
if err != nil {
return diag.FromErr(err)
}
Expand Down
Loading

0 comments on commit 4ad823b

Please sign in to comment.