İçindekiler
Python i18n: gettext'ten Modern Çeviri İş Akışlarına
Python, herhangi bir programlama dilinin en zengin i18n ekosistemlerinden birine sahiptir. Bu zenginlik; özellikle Django'nun küresel ölçekte konuşlandırılan web çerçevesi ve uluslararası toplulukları hedefleyen bilimsel hesaplama ile veri araçlarındaki Python kullanımı aracılığıyla büyük çok dilli uygulamalara yapılan onlarca yıllık yatırımla sağlanmıştır.
Bu kılavuz, Python i18n ortamının tamamını kapsamaktadır: standart kütüphanede yerleşik olan klasik gettext yaklaşımından Babel'in kapsamlı yerel veri kütüphanesine, Django ve Flask'a özgü çözümlerden Fluent kullanan modern yaklaşımlara kadar.
Python i18n Seçeneklerine Genel Bakış
| Kütüphane / Yaklaşım | En İyi Kullanım Alanı |
|---|---|
| gettext (stdlib) | Basit betikler ve uygulamalar |
| Babel | Sayı, tarih, para birimi biçimlendirme; PO dosyası yönetimi |
| Django i18n | Django web uygulamaları |
| Flask-Babel | Flask web uygulamaları |
| fluent.runtime | Mozilla'nın Fluent formatını kullanan uygulamalar |
| Babel + gettext | Üretimdeki çoğu Python web servisi |
gettext: Python'ın Yerleşik i18n Desteği
Python standart kütüphanesi, GNU gettext uluslararasılaştırma API'sini uygulayan gettext modülünü içerir. Bu modül, birçok üst düzey kütüphanenin üzerine inşa edildiği temel yapıdır.
gettext Nasıl Çalışır?
gettext, çeviri depolamak için .po (Portable Object) dosyaları kullanır ve bunları çalışma zamanında yüklemek için ikili .mo (Machine Object) dosyalarına derler.
İş Akışı:
- Kaynak kodda dizeleri
_()veyagettext()ile işaretleyin - İşaretli dizeleri
xgettextveyapybabelile.potşablonuna çıkarın - Şablondan dile özgü
.podosyaları oluşturun - Çevirmenler çevirileri tamamlar
.podosyalarını.modosyalarına derleyin- Kullanıcı yerel ayarına göre çalışma zamanında yükleyin
Temel gettext Kullanımı
import gettext
import locale
def setup_i18n(lang: str) -> gettext.GNUTranslations:
"""Load translations for the given language."""
translation = gettext.translation(
domain='messages',
localedir='locales',
languages=[lang],
fallback=True # Fall back to msgid (source string) if not found
)
return translation
# Setup for the application
trans = setup_i18n('fr')
_ = trans.gettext
ngettext = trans.ngettext # Plural-aware translation
# Usage
print(_("Hello, world!"))
print(_("Welcome, %(name)s!") % {"name": "Alice"})
# Plural forms
count = 3
print(ngettext(
"%(count)d item", # singular form
"%(count)d items", # plural form
count # the number that determines form
) % {"count": count})
Çıkarım İçin Dizeleri İşaretleme
# Direct marking with _()
title = _("Dashboard")
error = _("An error occurred: %(message)s") % {"message": str(e)}
# For strings that need to be defined before i18n is set up,
# use a deferred translation pattern:
def _(s):
return s # No-op at definition time
# These strings are extracted but not translated until runtime
ERROR_MESSAGES = {
"not_found": _("Resource not found"),
"unauthorized": _("You are not authorized to perform this action"),
}
# At runtime, translate with the actual _ function:
def get_error_message(key: str, translation_func) -> str:
return translation_func(ERROR_MESSAGES[key])
Babel: Kapsamlı Python i18n Kütüphanesi
Babel, sayılar, para birimleri, tarihler ve daha fazlası için tam CLDR tabanlı yerel veri desteğiyle gettext'i genişletir. Python'ın en kapsamlı i18n kütüphanesidir.
pip install Babel
Sayı ve Para Birimi Biçimlendirme
from babel.numbers import format_number, format_currency, format_percent
from babel import Locale
# Locale objects
en_us = Locale('en', 'US')
de_de = Locale('de', 'DE')
ja_jp = Locale('ja', 'JP')
# Number formatting
amount = 1234567.89
print(format_number(amount, locale='en_US')) # 1,234,567.89
print(format_number(amount, locale='de_DE')) # 1.234.567,89
print(format_number(amount, locale='en_IN')) # 12,34,567.89
# Currency formatting
print(format_currency(1234.56, 'USD', locale='en_US')) # $1,234.56
print(format_currency(1234.56, 'EUR', locale='de_DE')) # 1.234,56 €
print(format_currency(1234.56, 'JPY', locale='ja_JP')) # ¥1,235 (no decimals)
# Percentage
print(format_percent(0.8527, locale='en_US')) # 85%
print(format_percent(0.8527, '#.##%', locale='de_DE')) # 85,27%
Tarih ve Saat Biçimlendirme
from babel.dates import format_date, format_datetime, format_time, get_timezone
from datetime import datetime, date
dt = datetime(2024, 3, 15, 14, 30, 0)
d = date(2024, 3, 15)
# Date formats
print(format_date(d, locale='en_US')) # Mar 15, 2024
print(format_date(d, locale='de_DE')) # 15.03.2024
print(format_date(d, locale='ja_JP')) # 2024/03/15
print(format_date(d, format='full', locale='fr_FR')) # vendredi 15 mars 2024
# Datetime with timezone
tz = get_timezone('Europe/Berlin')
print(format_datetime(dt, locale='de_DE', tzinfo=tz))
# Relative time (e.g., "3 hours ago")
from babel.dates import format_timedelta
from datetime import timedelta
delta = timedelta(hours=-3)
print(format_timedelta(delta, locale='en_US', add_direction=True)) # 3 hours ago
print(format_timedelta(delta, locale='fr_FR', add_direction=True)) # il y a 3 heures
Babel ile PO Dosyası Yönetimi
Babel, çeviri dosyalarını yönetmek için bir komut satırı aracı olan pybabel'ı sağlar:
# 1. Extract translatable strings from Python source pybabel extract -F babel.cfg -o messages.pot . # babel.cfg configures which files to extract from: # [python: **.py] # [jinja2: **/templates/**.html] # 2. Initialize a new language (creates locales/fr/LC_MESSAGES/messages.po) pybabel init -i messages.pot -d locales -l fr # 3. After making source changes, update existing .po files pybabel update -i messages.pot -d locales # 4. Compile .po files to .mo files for runtime use pybabel compile -d locales
Babel/gettext'te Çoğul Formlar
# In .po file (French):
# msgid "%(count)d item"
# msgid_plural "%(count)d items"
# msgstr[0] "%(count)d élément"
# msgstr[1] "%(count)d éléments"
# In Python:
from babel.support import Translations
translations = Translations.load('locales', ['fr'])
ngettext = translations.ngettext
for count in [0, 1, 2, 10]:
msg = ngettext("%(count)d item", "%(count)d items", count) % {"count": count}
print(msg)
Django i18n
Django, şablonlara, modellere ve ORM'ye derinlemesine entegre edilmiş kapsamlı yerleşik i18n desteğine sahiptir.
Django i18n Kurulumu
# settings.py
LANGUAGE_CODE = 'en-us'
LANGUAGES = [
('en', 'English'),
('fr', 'Français'),
('de', 'Deutsch'),
('ja', '日本語'),
('ar', 'العربية'),
]
USE_I18N = True
USE_L10N = True # Locale-aware number/date formatting
USE_TZ = True
LOCALE_PATHS = [BASE_DIR / 'locale']
MIDDLEWARE = [
'django.middleware.locale.LocaleMiddleware', # Sets language from request
# ... other middleware
]
TEMPLATES = [{
'OPTIONS': {
'context_processors': [
'django.template.context_processors.i18n',
# ...
],
},
}]
Python Kodunda Django Çevirisi
from django.utils.translation import gettext as _, ngettext, gettext_lazy as _lazy
# Eager translation (evaluated immediately)
message = _("Welcome!")
# Lazy translation (evaluated when string is accessed - for model fields and class attrs)
class Article(models.Model):
title = models.CharField(max_length=200)
class Meta:
verbose_name = _lazy("article")
verbose_name_plural = _lazy("articles")
# Plural forms
def item_message(count: int) -> str:
return ngettext(
"You have %(count)d item",
"You have %(count)d items",
count
) % {"count": count}
# String formatting - use named parameters for translatability
def welcome(name: str) -> str:
return _("Welcome, %(name)s!") % {"name": name}
# Context-sensitive translation (same string, different meaning)
from django.utils.translation import pgettext
month = pgettext("month name", "May") # vs "May" as a verb
Django Şablon Çevirisi
{% load i18n %}
{# Simple translation #}
<h1>{% trans "Dashboard" %}</h1>
{# Translation with variables #}
{% blocktrans with name=user.first_name %}
Welcome, {{ name }}!
{% endblocktrans %}
{# Plural forms in templates #}
{% blocktrans count count=items|length %}
You have {{ count }} item.
{% plural %}
You have {{ count }} items.
{% endblocktrans %}
{# Language switcher #}
{% get_available_languages as LANGUAGES %}
<ul>
{% for lang_code, lang_name in LANGUAGES %}
<li>
<a href="/{{ lang_code }}/">{{ lang_name }}</a>
</li>
{% endfor %}
</ul>
Django URL i18n Kalıpları
# urls.py
from django.conf.urls.i18n import i18n_patterns
from django.urls import path, include
urlpatterns = [
path('i18n/', include('django.conf.urls.i18n')), # Language switcher endpoint
]
urlpatterns += i18n_patterns(
# These URLs get a language prefix: /en/about/, /fr/about/
path('about/', views.about, name='about'),
path('products/', include('products.urls')),
prefix_default_language=False, # /about/ redirects to /en/about/
)
Flask-Babel ile Flask i18n
Flask-Babel, Babel'in gücünü Flask uygulamalarına taşır:
pip install Flask-Babel
# app.py
from flask import Flask, g, request
from flask_babel import Babel, _, ngettext, format_currency, format_datetime
app = Flask(__name__)
app.config['BABEL_DEFAULT_LOCALE'] = 'en'
app.config['BABEL_DEFAULT_TIMEZONE'] = 'UTC'
app.config['BABEL_TRANSLATION_DIRECTORIES'] = 'translations'
def get_locale():
# 1. Check URL parameter
lang = request.args.get('lang')
if lang:
return lang
# 2. Check user preference in session/database
if hasattr(g, 'current_user') and g.current_user.language:
return g.current_user.language
# 3. Use Accept-Language header
return request.accept_languages.best_match(['en', 'fr', 'de', 'ja'])
babel = Babel(app, locale_selector=get_locale)
@app.route('/products/<int:product_id>')
def product_detail(product_id):
product = Product.query.get_or_404(product_id)
# These use the current locale automatically
price = format_currency(product.price, 'USD')
created = format_datetime(product.created_at, format='medium')
return render_template('product.html',
product=product,
price=price,
created=created,
title=_("Product: %(name)s") % {"name": product.name}
)
Modern Yaklaşım: Python için Fluent
Mozilla'nın Fluent'i Python'da fluent.runtime aracılığıyla kullanılabilir:
pip install fluent.runtime
from fluent.runtime import FluentBundle, FluentResource
# Load and use Fluent files
def create_bundle(locale: str, ftl_content: str) -> FluentBundle:
bundle = FluentBundle([locale])
resource = FluentResource(ftl_content)
errors = bundle.add_resource(resource)
if errors:
raise ValueError(f"FTL errors: {errors}")
return bundle
# FTL file content
en_ftl = """
welcome = Welcome to our app!
greeting = Hello, { $name }!
items =
{ $count ->
[0] No items
[one] { $count } item
*[other] { $count } items
}
"""
bundle = create_bundle("en-US", en_ftl)
def translate(bundle: FluentBundle, message_id: str, **kwargs) -> str:
msg = bundle.get_message(message_id)
if not msg or not msg.value:
return message_id
value, errors = bundle.format_pattern(msg.value, kwargs)
return value
print(translate(bundle, "welcome"))
print(translate(bundle, "greeting", name="Alice"))
print(translate(bundle, "items", count=0))
print(translate(bundle, "items", count=1))
print(translate(bundle, "items", count=5))
CI/CD ile Entegrasyon
Python i18n iş akışları, sürekli yerelleştirme süreçleriyle doğal biçimde entegre olur:
# .github/workflows/i18n.yml
name: i18n
on:
push:
paths:
- '**.py'
- '**/templates/**.html'
jobs:
extract-and-update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Babel
run: pip install Babel
- name: Extract strings
run: pybabel extract -F babel.cfg -o messages.pot .
- name: Update translation files
run: pybabel update -i messages.pot -d locales
- name: Push to translation platform
run: |
# Upload messages.pot to your TMS
curl -X POST https://api.better-i18n.com/upload \
-F "file=@messages.pot" \
-H "Authorization: Bearer ${{ secrets.BETTER_I18N_API_KEY }}"
Kapsamlı CI/CD i18n kalıpları için bkz. i18n CI/CD pipeline otomasyonu.
Yerel Ayar Algılama İçin En İyi Uygulamalar
from babel import Locale, UnknownLocaleError
from typing import Optional
SUPPORTED_LOCALES = ['en', 'fr', 'de', 'ja', 'ar', 'pt-BR']
def parse_accept_language(header: str) -> list[str]:
"""Parse Accept-Language header into ordered list of language codes."""
locales = []
for part in header.split(','):
parts = part.strip().split(';')
lang = parts[0].strip()
# Extract quality (q=0.9) or default to 1.0
q = 1.0
for param in parts[1:]:
if param.strip().startswith('q='):
try:
q = float(param.strip()[2:])
except ValueError:
pass
locales.append((lang, q))
# Sort by quality, highest first
locales.sort(key=lambda x: x[1], reverse=True)
return [lang for lang, _ in locales]
def negotiate_locale(accept_language: str, supported: list[str] = SUPPORTED_LOCALES) -> str:
"""Find the best matching supported locale for the given Accept-Language header."""
requested = parse_accept_language(accept_language)
for requested_lang in requested:
# Exact match first
if requested_lang in supported:
return requested_lang
# Language-only match (en-US → en)
base = requested_lang.split('-')[0]
if base in supported:
return base
# Find any supported locale with the same base
for supported_locale in supported:
if supported_locale.startswith(base + '-'):
return supported_locale
return 'en' # Default fallback
Diller arası çoğullama kurallarına daha ayrıntılı bakmak için bkz. diller arası çoğullama kuralları. Çeviri yönetim ekosistemine genel bir bakış için bkz. çeviri yönetim sistemleri.
Uygulamanızı better-i18n ile Küreselleştirin
better-i18n; yapay zeka destekli çeviriler, git-native iş akışları ve küresel CDN teslimatını tek bir geliştirici odaklı platformda bir araya getirir. Elektronik tablolar yönetmeyi bırakın ve her dilde yayınlamaya başlayın.
Ücretsiz başlayın → · Özellikleri keşfedin · Belgeleri okuyun