In the realm of web development, supporting multiple languages is crucial for reaching a broader audience. This is where Transify, our Python library, comes into play. It caters to developers aiming to easily add translation capabilities to their applications. Whether you’re using frameworks like FastAPI or Flask, Transify integrates seamlessly, allowing you to manage translations using JSON files efficiently.

Why Choose Transify?

Transify stands out by offering an easy setup for multi-language support. This means you can maintain translation files cleanly without getting bogged down by complex configurations. It’s especially beneficial for web applications that need dynamic, on-the-fly translations.

Key Features

  1. Multiple Language Support
    Effortlessly extend your application’s reach by adding translation JSON files for each language. This modular approach keeps your languages organized and manageable.

  2. Dynamic Replacements
    Transify isn’t just about static text. With dynamic variable insertion, you can create flexible translations that adapt to different contexts.

  3. Nested Translation Keys
    Organize your translations with nested keys like captions.hello, enhancing readability and structure.

  4. Locale Switching
    Change the active language of your application instantly, providing a personalized experience for each user.

  5. Integration with Jinja2 Templates
    Utilize real-time translations directly in your HTML templates, enabling a smooth frontend-user experience.

Getting Started with Transify

Installation

You can install Transify using either pip or Poetry, depending on your preference:

Using pip:

pip install transify

Using Poetry:

poetry add transify

Organizing Your Translation Files

  1. Create Language Folders
    First, set up a directory named lang in your project to store translation files.

  2. Add Translation Files
    Within the lang directory, create subfolders for each language code (e.g., enfa). Populate these with your translation JSON files.

Example Directory Structure:

lang/
  en/
    captions.json
    messages.json
    validations.json
  fa/
    captions.json
    messages.json
    validations.json

Sample JSON Translation Files

captions.json

{
  "name": "Azadjalal",
  "first_name": "First name",
  "hello": "Hello",
  "goodbye": "Goodbye",
  "username": "username",
  "good_score": "Good score"
}

messages.json

{
  "welcome": "Welcome, :name to my website :website.",
  "errors": {
    "create": "Create item failed.",
    "user": {
      "not_found": "The user not found."
    }
  }
}

validations.json

{
  "required": "The :attr is required.",
  "between": "The :attr must be between :min and :max values."
}

Integrating Transify into a FastAPI Project

Here’s a complete example of integrating Transify into a FastAPI application:

import pathlib

import uvicorn
from fastapi import FastAPI
from fastapi.requests import Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates

from transify import *

app = FastAPI(title='transify', version='1.0.0')

# Mount static resources
app.mount(
    '/resources',
    StaticFiles(directory=f'{pathlib.Path(__file__).resolve().parent}/resources', html=False),
    name='static'
)

# Jinja2 template setup
templates = Jinja2Templates(
    directory=f'{pathlib.Path(__file__).resolve().parent}/resources'
)

# Update the template environment with the trans function
templates.env.globals.update(trans=trans)

def startup():
    load_languages(path='tests/lang', default_fallback='en')

app.add_event_handler('startup', startup)

@app.get('/', response_class=HTMLResponse)
async def get_home(request: Request):
    return templates.TemplateResponse('home.html', {'request': request, 'lang': get_locale()})

if __name__ == '__main__':
    uvicorn.run('test_webapp:app', host='localhost', port=8000, reload=True)

Using Transify in Jinja2 Templates

Embed translations in your HTML templates like this:

<!DOCTYPE html>
<html lang={{ lang }}>
    <head>
        <title>Transify Example</title>
    </head>
    <body>
        <!-- Example: Display translation for 'hello' -->
        <span>{{ trans('captions.hello') }}</span> <!-- Outputs: Hello -->
    </body>
    <footer>
        <!-- Example: Display translation for 'welcome' -->
        {{ trans('messages.welcome', 'name:Azadjalal|website:azadjalal.ir') }} 
        <!-- Outputs: Welcome, Azadjalal to my website azadjalal.ir -->
    </footer>
</html>

Core Functions and Methods

  • load_languages(path: str = 'lang', default_fallback: str = 'en')
    Loads translation files from the specified path, setting up different languages and a default fallback.

  • set_locale(locale: str)
    Switches the active language to the specified locale, e.g., between ‘en’ and ‘fa’.

    set_locale('fa')
    
  • get_locale()
    Retrieves the currently active locale.

  • trans(key: str, value: str = None)
    Core function for fetching translations. Handles dynamic replacements if a value is provided.

    # Simple translation
    trans('captions.hello')  # Output: "Hello"
    
    # With dynamic replacements
    trans('messages.welcome', value='name:Azadjalal|website:azadjalal.ir')  
    # Output: "Welcome, Azadjalal to my website azadjalal.ir."
    

Testing Your Integration

To ensure everything works as expected, use the provided test_transify.py file. This script employs Python’s unittest framework to test key functionalities like language loading, locale switching, and dynamic translations.

Run the tests with:

python test_transify.py

Sample Test Cases

  1. Load Languages

    Test that languages are loaded correctly:

    self.assertIn('en', lang.languages)
    self.assertIn('fa', lang.languages)
    
  2. Set Locale

    Validate locale switching:

    set_locale('fa')
    self.assertEqual(get_locale(), 'fa')
    set_locale('en')
    
  3. Basic Translation

    Ensure basic translations are accurate:

    self.assertEqual(trans('captions.hello'), 'Hello')
    
  4. Dynamic Replacements

    Verify translations with dynamic content:

    self.assertEqual(
        trans('validations.between', value='score|min:1|max:20'),
        'The score must be between 1 and 20 values.'
    )
    
  5. Nested Keys

    Check translations using nested keys:

    self.assertEqual(trans('messages.errors.user.not_found'), 'The user not found.')
    
  6. Locale-Specific Translations

    Test different locales:

    set_locale('fa')
    self.assertEqual(trans('captions.hello'), 'سلام')
    

Error Handling and Customization

Transify elegantly handles missing keys or errors during translation by logging an error and returning the original key as a fallback, safeguarding your application from crashes.

trans('messages.nonexistent')  # Returns 'messages.nonexistent'

Customize the load_languages function to fit your directory structure or project setup, allowing flexibility and ease of use.

Conclusion

Transify simplifies the process of managing multiple languages in your web application. With its intuitive setup and robust features for dynamic translations, it seamlessly integrates into Jinja2 templates and various web frameworks. Whether you’re developing with FastAPI or another environment, Transify offers a comprehensive solution for language management.

Explore the source code on GitHub and get started today with Transify to enhance your application’s global reach!