drf-localize 0.1.5
Package to provide localization experiences for mobile and api applications.
License: MIT
Requires Python: >=3.7,<=3.10

# 🌐 DRF Localize

Package to provide localization experiences for mobile and api applications.

# ⚡️ Features

✅ Localize keys for multiple languages<br/>
✅ Generate localizable `iOS` , `Android`, `Web` compatible **zip** & **files**<br/>
✅ Model `JSON` based fields localization<br/>
✅ Library for localizable keys<br/>
✅ REST API for localizable keys<br/>
✅ Configurable

# 🔐 Installation

Use the package manager [pip](https://pip.pypa.io/en/stable/) to install drf_localize.

```bash
pip install drf_localize
```

then add it to your installed apps:

```python
INSTALLED_APPS = [
    ...,
    'drf_localize',
    ...,
]
```

then run migrate:

```bash
python manage.py migrate
```

and load initial data:

```bash
python manage.py loaddata localizelanguages
python manage.py loaddata localizekeys (optional)
```

# 🔨 Configuration

Configuration for **DRF Localize** is namespaced in a single django setting, named `DRF_LOCALIZE`, by default everything is configured out of the box.

```python
DRF_LOCALIZE = {
    'LANGUAGES': 'ALL',  # noqa
    'BASE_DIR': BASE_DIR,  # noqa
    'API_KEY_HEADER_NAME': 'HTTP_X_API_KEY',
    'PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'KEY_MODEL_CLASS': 'drf_localize.models.LocalizeKey',
    'LANGUAGE_MODEL_CLASS': 'drf_localize.models.LocalizeLanguage',
    'LANGUAGE_MODEL_CLASS_FIELD': 'code',
}
```

# 🔎 Available Settings

| Header                       | Description                                                           | Default                                         |
|:-----------------------------|:----------------------------------------------------------------------|:------------------------------------------------|
| `LANGUAGES`                  | **Specify language codes to use as array**.                           | ALL                                             |
| `BASE_DIR`                   | **Specify internal directory path**.                                  | BASE_DIR                                        |
| `API_KEY_HEADER_NAME`        | **Specify `X-API-Key` header**.                                       | HTTP_X_API_KEY                                  |
| `PAGINATION_CLASS`           | **Specify pagination class**.                                         | rest_framework.pagination.PageNumberPagination  |
| `KEY_MODEL_CLASS`            | **Specify key model class, must comply to `drf_localize` key model**. | drf_localize.models.LocalizeKey                 |
| `LANGUAGE_MODEL_CLASS`       | **Specify language model class**.                                     | drf_localize.models.LocalizeLanguage            |
| `LANGUAGE_MODEL_CLASS_FIELD` | **Specify language model class code field**.                          | code                                            |

# 🔧 Usage

Specify `localize` fields in your model

```python
from django.db import models


class Blog(models.Model):  # noqa

    # DRF Localize model specific constant, which is used to translate model fields
    LOCALIZE_TRANSLATE = ['title']  # or LOCALIZE_TRANSLATE = ['title', 'description']

    # DRF Localize model specific constant, which is used to store translations (json)
    LOCALIZE_FIELD = 'i18n'
    
    # DRF Localize model specific constant, which is used to auto-update or remove translations for languages
    LOCALIZE_AUTO_UPDATE = False # not required , defaults to False

    # Your custom model fields
    title = models.CharField(max_length=254, null=True)
    description = models.CharField(max_length=512, null=True)

    # Referenced json field to store translations
    i18n = models.JSONField(default=dict)
```

Inherit `I18NModelSerializer` in your model serializer

```python
from apps.blog.models import Blog  # noqa
from drf_localize.serializers import (
    I18NModelSerializer
)


class BlogSerializer(I18NModelSerializer):
    class Meta:
        model = Blog
        fields = '__all__'

```

Use your model serializer in your endpoints as usual

```python
from apps.blog.models import Blog  # noqa
from apps.blog.serializers import BlogSerializer  # noqa
from rest_framework.viewsets import GenericViewSet
from rest_framework.generics import (
    ListAPIView,
    CreateAPIView,
    RetrieveUpdateDestroyAPIView,
)


class BlogViewSet(ListAPIView, CreateAPIView, RetrieveUpdateDestroyAPIView, GenericViewSet):
    queryset = Blog.objects.all()
    serializer_class = BlogSerializer

```

# 📈 REST API

### Standalone mode

If you are planning to use `drf_localize`'s REST API endpoints add package urls in your project.

```python
from django.urls import (include, path, )

urlpatterns = [
    ...,
    path('drf_localize', include('drf_localize.urls', namespace='localize')),
    ...,
]
```

### Create localize key

```http
POST /drf_localize/localize/keys
```

| Parameter | Type     | Description            |
|:----------|:---------|:-----------------------|
| `code`    | `string` | **Required**. *Unique* |
| `i18n`    | `object` | **Optional**.          |

```json
{
  "code": "DRF Localize",
  "i18n": {
    "en": "DRF Localize",
    "ro": "DRF Localizare",
    "ru": "DRF Локализация"
  }
}
```

#### Update localize key

```http
PATCH /drf_localize/localize/keys/:id
```

| Parameter | Type     | Description         |
|:----------|:---------|:--------------------|
| `:id`     | `string` | **Required**. |
| `code`    | `string` | **Required**. *Unique* |
| `i18n`    | `object` | **Optional**.       |

```json
{
  "i18n": {
    "en": "DRF Localizes",
    "ro": "DRF Localizare",
    "ru": "DRF Локализация"
  }
}
```

### Create localize namespace key

```http
POST /drf_localize/localize/keys
```

| Parameter | Type     | Description            |
|:----------|:---------|:-----------------------|
| `code`    | `string` | **Required**. *Unique* |
| `i18n`    | `object` | **Optional**.          |
| `type`    | `string` | **Required**.          |

```json
{
  "code": "global",
  "i18n": {
    "en": {
      "localize": "DRF Localizes"
    },
    "ro": {
      "localize": "DRF Localizare"
    },
    "ru": {
      "localize": "DRF Локализация"
    }
  },
  "type": "NAMESPACE"
}
```

#### Update localize namespace key

```http
PATCH /drf_localize/localize/keys/:id
```

| Parameter | Type     | Description         |
|:----------|:---------|:--------------------|
| `:id`     | `string` | **Required**. |
| `code`    | `string` | **Required**. *Unique* |
| `i18n`    | `object` | **Optional**.       |
| `type`    | `string` | **Required**.          |

```json
{
  "i18n": {
    "en": {
      "localize": "DRF Localize"
    },
    "ro": {
      "localize": "DRF Localizare"
    },
    "ru": {
      "localize": "DRF Локализация"
    }
  },
  "type": "NAMESPACE"
}
```

#### Retrieve localize namespace keys

```http
GET /drf_localize/localize/keys?search=NAMESPACE
```

#### Download platform specific localize key file

```http
GET /drf_localize/localize/keys/:platform/:language/file
```

| Parameter   | Type     | Description                                 |
|:------------|:---------|:--------------------------------------------|
| `:platform` | `string` | **Required**. `ios`/ `android` / `web`      |
| `:language` | `string` | **Required**. `en`/ any other language code |

#### Download platform specific localize keys zip file

```http
GET /drf_localize/localize/keys/:platform/zip
```

| Parameter   | Type     | Description                                 |
|:------------|:---------|:--------------------------------------------|
| `:platform` | `string` | **Required**. `ios`/ `android` / `web`      |

#### Download all platform specific localize keys in a single zip file

```http
GET /drf_localize/localize/keys/zip
```

### Service mode

You will need to add a middleware class:

```python
# This middleware listens to `X-API-Key` key header value and finds your application.

MIDDLEWARE = [
    ...,
    'drf_localize.middlewares.LocalizeApplicationMiddleware',
    ...,
]
```

then add `X-API-Key` header in standalone mode endpoints:

| Header         | Type    | Description                        |
|:---------------|:--------|:-----------------------------------|
| `X-API-Key`| `string`| **Required**. Your application key |

### Create application

```http
POST /drf_localize/localize/applications
```

| Parameter | Type     | Description         |
|:----------|:---------|:--------------------|
| `title`   | `string` | **Optional**.|
| `description`    | `string` | **Optional**.       |

#### Retrieve application

```http
GET /drf_localize/localize/application
```

| Header         | Type    | Description                        |
|:---------------|:--------|:-----------------------------------|
| `X-API-Key`| `string`| **Required**. Your application key |

#### Attach application languages

```http
POST /drf_localize/localize/application/languages
```

| Parameter | Type     | Description   |
|:----------|:---------|:--------------|
| `languages_id`   | `array`  | **Required**. |

| Header         | Type    | Description                        |
|:---------------|:--------|:-----------------------------------|
| `X-API-Key`| `string`| **Required**. Your application key |

```json
{
  "languages_id": [
    2,
    1
  ]
}
```

#### Detach application languages

```http
DELETE /drf_localize/localize/application/languages
```

| Parameter | Type     | Description   |
|:----------|:---------|:--------------|
| `languages_id`   | `array`  | **Required**. |

| Header         | Type    | Description                        |
|:---------------|:--------|:-----------------------------------|
| `X-API-Key`| `string`| **Required**. Your application key |

```json
{
  "languages_id": [
    1
  ]
}
```

#### Retrieve application languages

```http
GET /drf_localize/localize/application/languages
```

| Header         | Type    | Description                        |
|:---------------|:--------|:-----------------------------------|
| `X-API-Key`| `string`| **Required**. Your application key |

```json
[
  {
    "code": "ab",
    "name": "Abkhaz",
    "native": "аҧсуа"
  }
]
```

# 📦️ Library

```python
from drf_localize import localize

# Set translatable key translation.
localize.set_key('Welcome').set_en('Welcome').set_ro('Bine ati venit').set_pt('Receber')  # noqa

# Set translatable namespace translation.
localize.set_key_namespace('common').set_en({
    'welcome': 'Welcome', 'order': 'Order'
}).set_ro({
    'welcome': 'Bine ati venit', 'order': 'Ordin'
}).set_pt({
    'welcome': 'Receber', 'order': 'Pedido'
})

# Build 'en' translation file for iOS
localize.build(language='en')
file = localize.to_platform(platform='IOS')  # noqa

# Build translations zip file for iOS
file = localize.build_zip(platform='IOS')  # noqa

# Build translations zip file for ANDROID
file = localize.build_zip(platform='ANDROID')  # noqa

# Build translations zip file for WEB
file = localize.build_zip(platform='WEB')  # noqa

# Build translations zip file for every platform
file = localize.build_zip()  # noqa
```

## 📌 Library helpers

```python

# from drf_localize import localize_key_type

class LocalizeKeyType:
    KEY_NAMESPACE = 'NAMESPACE'
    KEY_PLAIN = 'PLAIN'

    KEY_TYPES = [KEY_NAMESPACE, KEY_PLAIN]
    KEY_CHOICES = (
        (KEY_NAMESPACE, KEY_NAMESPACE),
        (KEY_PLAIN, KEY_PLAIN)
    )


# from drf_localize import localize_platform

class LocalizePlatform:
    PLATFORM_IOS = 'IOS'
    PLATFORM_ANDROID = 'ANDROID'
    PLATFORM_WEB = 'WEB'

    PLATFORM_TYPES = [PLATFORM_IOS, PLATFORM_ANDROID, PLATFORM_WEB]
    PLATFORM_CHOICES = (
        (PLATFORM_IOS, PLATFORM_IOS),
        (PLATFORM_ANDROID, PLATFORM_ANDROID),
        (PLATFORM_WEB, PLATFORM_WEB)
    )
```

# ⚗️ Internal Serializer Classes

Uses `I18N` serializer to transform localize field

> **Inherit this `I18NModelSerializer` serializer in your model serializer**

```python
from rest_framework.serializers import ModelSerializer


class I18NModelSerializer(ModelSerializer):  # noqa
    ...
```

Transforms localize field into i18n json field with translations

```python
from rest_framework.serializers import Serializer


class I18N(Serializer):  # noqa
    ...
```

# 👥 Contributing

Pull requests are always appreciated. Open issues addressing pull requests.

Flow:

1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Test your changes to the best of your ability.
4. Update the documentation to reflect your changes if they add or changes current functionality.
5. Commit your changes (`git commit -am 'Added some feature'`).
6. Push to the branch (`git push origin my-new-feature`)
7. Create new Pull Request