diff --git a/.gitignore b/.gitignore index 96288a1..8dedb1e 100644 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,6 @@ coverage.xml cover/ # Translations -*.mo *.pot # Django stuff: diff --git a/CHANGELOG.md b/CHANGELOG.md index dd325cc..bfbb9aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - feat: Add audit log history view to Django Admin. ([#743](https://github.com/jazzband/django-auditlog/pull/743)) - Fix Expression test compatibility for Django 6.0+ ([#759](https://github.com/jazzband/django-auditlog/pull/759)) +- Add I18N Support ([#762](https://github.com/jazzband/django-auditlog/pull/762)) ## 3.2.1 (2025-07-03) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..152119d --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +# Django Auditlog Makefile + +# Default target shows help +.DEFAULT_GOAL := help +.PHONY: help install test makemessages compilemessages create-locale i18n clean + +# Variables +AUDITLOG_DIR := auditlog + +install: ## Install dependencies + pip install -e . + +test: ## Run tests + ./runtests.sh + +makemessages: ## Extract translatable strings and create/update .po files for all languages + cd $(AUDITLOG_DIR) && \ + django-admin makemessages --add-location=file -a --ignore=__pycache__ --ignore=migrations + +compilemessages: ## Compile all translation files (.po to .mo) + cd $(AUDITLOG_DIR) && \ + django-admin compilemessages + +create-locale: ## Create initial locale structure for a new language (requires LANG=) + @if [ -z "$(LANG)" ]; then \ + echo "Error: LANG parameter is required. Usage: make create-locale LANG="; \ + echo "Examples: make create-locale LANG=ko, make create-locale LANG=ja"; \ + exit 1; \ + fi + mkdir -p $(AUDITLOG_DIR)/locale/$(LANG)/LC_MESSAGES + cd $(AUDITLOG_DIR) && \ + django-admin makemessages --add-location=file -l $(LANG) --ignore=__pycache__ --ignore=migrations + +i18n: makemessages compilemessages ## Full i18n workflow: extract strings, compile messages + +clean: ## Clean compiled translation files (.mo files) + find $(AUDITLOG_DIR)/locale -name "*.mo" -delete + +help: ## Help message for targets + @echo "Available commands:" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \ + | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/auditlog/locale/ja/LC_MESSAGES/django.mo b/auditlog/locale/ja/LC_MESSAGES/django.mo new file mode 100644 index 0000000..42c5020 Binary files /dev/null and b/auditlog/locale/ja/LC_MESSAGES/django.mo differ diff --git a/auditlog/locale/ja/LC_MESSAGES/django.po b/auditlog/locale/ja/LC_MESSAGES/django.po new file mode 100644 index 0000000..eb01931 --- /dev/null +++ b/auditlog/locale/ja/LC_MESSAGES/django.po @@ -0,0 +1,192 @@ +# Django Auditlog Japanese Translation +# Copyright (C) 2025 Django Auditlog Contributors +# This file is distributed under the same license as the django-auditlog package. +# Youngkwang Yang , 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: django-auditlog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-09-28 03:43+0900\n" +"PO-Revision-Date: 2025-09-28 03:16+0900\n" +"Last-Translator: Youngkwang Yang \n" +"Language-Team: Japanese \n" +"Language: ja\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: admin.py mixins.py +msgid "Changes" +msgstr "変更" + +#: apps.py mixins.py templates/auditlog/object_history.html +msgid "Audit log" +msgstr "監査ログ" + +#: filters.py +msgid "Resource Type" +msgstr "リソースタイプ" + +#: filters.py models.py +msgid "Correlation ID" +msgstr "Correlation ID" + +#: mixins.py +msgid "Click to filter by records with this correlation id" +msgstr "このCorrelation IDでレコードをフィルタするにはクリックしてください" + +#: mixins.py +msgid "Created" +msgstr "作成済み" + +#: mixins.py +msgid "User" +msgstr "ユーザー" + +#: mixins.py +msgid "Resource" +msgstr "リソース" + +#: mixins.py +#, python-format +msgid "Audit log: %s" +msgstr "監査ログ:%s" + +#: mixins.py +msgid "View" +msgstr "表示" + +#: models.py +msgid "create" +msgstr "作成" + +#: models.py +msgid "update" +msgstr "更新" + +#: models.py +msgid "delete" +msgstr "削除" + +#: models.py +msgid "access" +msgstr "アクセス" + +#: models.py +msgid "content type" +msgstr "コンテンツタイプ" + +#: models.py +msgid "object pk" +msgstr "オブジェクトPK" + +#: models.py +msgid "object id" +msgstr "オブジェクトID" + +#: models.py +msgid "object representation" +msgstr "オブジェクト表現" + +#: models.py +msgid "action" +msgstr "アクション" + +#: models.py +msgid "change message" +msgstr "変更メッセージ" + +#: models.py +msgid "actor" +msgstr "アクター" + +#: models.py +msgid "remote address" +msgstr "リモートアドレス" + +#: models.py +msgid "remote port" +msgstr "リモートポート" + +#: models.py +msgid "timestamp" +msgstr "タイムスタンプ" + +#: models.py +msgid "additional data" +msgstr "追加データ" + +#: models.py +msgid "actor email" +msgstr "アクターメール" + +#: models.py +msgid "log entry" +msgstr "ログエントリ" + +#: models.py +msgid "log entries" +msgstr "ログエントリ" + +#: models.py +msgid "Created {repr:s}" +msgstr "{repr:s}が作成されました" + +#: models.py +msgid "Updated {repr:s}" +msgstr "{repr:s}が更新されました" + +#: models.py +msgid "Deleted {repr:s}" +msgstr "{repr:s}が削除されました" + +#: models.py +msgid "Logged {repr:s}" +msgstr "{repr:s}がログに記録されました" + +#: render.py +msgid "Field" +msgstr "フィールド" + +#: render.py +msgid "From" +msgstr "変更前の値" + +#: render.py +msgid "To" +msgstr "変更後の値" + +#: render.py +msgid "Relationship" +msgstr "関係" + +#: render.py +msgid "Action" +msgstr "アクション" + +#: render.py +msgid "Objects" +msgstr "オブジェクト一覧" + +#: templates/auditlog/entry_detail.html +msgid "system" +msgstr "システム" + +#: templates/auditlog/entry_detail.html +msgid "No field changes" +msgstr "フィールドの変更なし" + +#: templates/auditlog/object_history.html +msgid "Home" +msgstr "ホーム" + +#: templates/auditlog/object_history.html +msgid "No log entries found." +msgstr "ログエントリが見つかりません。" + +#: templates/auditlog/pagination.html +msgid "entry" +msgid_plural "entries" +msgstr[0] "ログエントリ" diff --git a/auditlog/locale/ko/LC_MESSAGES/django.mo b/auditlog/locale/ko/LC_MESSAGES/django.mo new file mode 100644 index 0000000..6a57ad9 Binary files /dev/null and b/auditlog/locale/ko/LC_MESSAGES/django.mo differ diff --git a/auditlog/locale/ko/LC_MESSAGES/django.po b/auditlog/locale/ko/LC_MESSAGES/django.po new file mode 100644 index 0000000..12a931c --- /dev/null +++ b/auditlog/locale/ko/LC_MESSAGES/django.po @@ -0,0 +1,192 @@ +# Django Auditlog Korean Translation +# Copyright (C) 2025 Django Auditlog Contributors +# This file is distributed under the same license as the django-auditlog package. +# Youngkwang Yang , 2025. +# +msgid "" +msgstr "" +"Project-Id-Version: django-auditlog\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-09-28 03:43+0900\n" +"PO-Revision-Date: 2025-09-28 02:55+0900\n" +"Last-Translator: Youngkwang Yang \n" +"Language-Team: Korean \n" +"Language: ko\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: admin.py mixins.py +msgid "Changes" +msgstr "변경 사항" + +#: apps.py mixins.py templates/auditlog/object_history.html +msgid "Audit log" +msgstr "감사 로그" + +#: filters.py +msgid "Resource Type" +msgstr "리소스 타입" + +#: filters.py models.py +msgid "Correlation ID" +msgstr "Correlation ID" + +#: mixins.py +msgid "Click to filter by records with this correlation id" +msgstr "이 Correlation ID로 레코드를 필터링하려면 클릭하세요" + +#: mixins.py +msgid "Created" +msgstr "생성됨" + +#: mixins.py +msgid "User" +msgstr "사용자" + +#: mixins.py +msgid "Resource" +msgstr "리소스" + +#: mixins.py +#, python-format +msgid "Audit log: %s" +msgstr "감사 로그: %s" + +#: mixins.py +msgid "View" +msgstr "보기" + +#: models.py +msgid "create" +msgstr "생성" + +#: models.py +msgid "update" +msgstr "수정" + +#: models.py +msgid "delete" +msgstr "삭제" + +#: models.py +msgid "access" +msgstr "접근" + +#: models.py +msgid "content type" +msgstr "콘텐츠 타입" + +#: models.py +msgid "object pk" +msgstr "객체 PK" + +#: models.py +msgid "object id" +msgstr "객체 ID" + +#: models.py +msgid "object representation" +msgstr "객체 표현" + +#: models.py +msgid "action" +msgstr "작업" + +#: models.py +msgid "change message" +msgstr "변경 메시지" + +#: models.py +msgid "actor" +msgstr "작업자" + +#: models.py +msgid "remote address" +msgstr "원격 주소" + +#: models.py +msgid "remote port" +msgstr "원격 포트" + +#: models.py +msgid "timestamp" +msgstr "타임스탬프" + +#: models.py +msgid "additional data" +msgstr "추가 데이터" + +#: models.py +msgid "actor email" +msgstr "작업자 이메일" + +#: models.py +msgid "log entry" +msgstr "로그 항목" + +#: models.py +msgid "log entries" +msgstr "로그 항목들" + +#: models.py +msgid "Created {repr:s}" +msgstr "{repr:s}이(가) 생성됨" + +#: models.py +msgid "Updated {repr:s}" +msgstr "{repr:s}이(가) 수정됨" + +#: models.py +msgid "Deleted {repr:s}" +msgstr "{repr:s}이(가) 삭제됨" + +#: models.py +msgid "Logged {repr:s}" +msgstr "{repr:s}이(가) 기록됨" + +#: render.py +msgid "Field" +msgstr "필드" + +#: render.py +msgid "From" +msgstr "변경 전" + +#: render.py +msgid "To" +msgstr "변경 후" + +#: render.py +msgid "Relationship" +msgstr "관계" + +#: render.py +msgid "Action" +msgstr "작업" + +#: render.py +msgid "Objects" +msgstr "객체" + +#: templates/auditlog/entry_detail.html +msgid "system" +msgstr "시스템" + +#: templates/auditlog/entry_detail.html +msgid "No field changes" +msgstr "필드 변경 사항 없음" + +#: templates/auditlog/object_history.html +msgid "Home" +msgstr "홈" + +#: templates/auditlog/object_history.html +msgid "No log entries found." +msgstr "로그 항목을 찾을 수 없습니다." + +#: templates/auditlog/pagination.html +msgid "entry" +msgid_plural "entries" +msgstr[0] "항목" diff --git a/auditlog/render.py b/auditlog/render.py index 6c9c3ee..bae9600 100644 --- a/auditlog/render.py +++ b/auditlog/render.py @@ -2,6 +2,7 @@ from django.core.exceptions import FieldDoesNotExist from django.forms.utils import pretty_name from django.utils.html import format_html, format_html_join from django.utils.safestring import mark_safe +from django.utils.translation import gettext_lazy as _ def render_logentry_changes_html(log_entry): @@ -60,7 +61,7 @@ def get_field_verbose_name(log_entry, field_name): def _render_field_changes(log_entry, atom_changes): rows = [] - rows.append(_format_header("#", "Field", "From", "To")) + rows.append(_format_header("#", _("Field"), _("From"), _("To"))) for i, (field, change) in enumerate(sorted(atom_changes.items()), 1): field_name = get_field_verbose_name(log_entry, field) @@ -72,7 +73,7 @@ def _render_field_changes(log_entry, atom_changes): def _render_m2m_changes(log_entry, m2m_changes): rows = [] - rows.append(_format_header("#", "Relationship", "Action", "Objects")) + rows.append(_format_header("#", _("Relationship"), _("Action"), _("Objects"))) for i, (field, change) in enumerate(sorted(m2m_changes.items()), 1): field_name = get_field_verbose_name(log_entry, field) diff --git a/auditlog/templates/auditlog/entry_detail.html b/auditlog/templates/auditlog/entry_detail.html index 9cd45fa..b0c34df 100644 --- a/auditlog/templates/auditlog/entry_detail.html +++ b/auditlog/templates/auditlog/entry_detail.html @@ -4,7 +4,7 @@