From 65ebec6663b869a38b7974b360ab0a2174b1d030 Mon Sep 17 00:00:00 2001 From: Youngkwang Yang Date: Tue, 30 Sep 2025 23:15:45 +0900 Subject: [PATCH] Add I18N Support (#762) * Add locale files * Add Korean translations * Update translation files using LLM * Add missing gettext wrappers for UI strings * Add i18n support for audit log table headers * Add justfile for i18n workflow * Add `.mo` binary file * Update CHANGELOG.md * Add create-locale command to justfile for new language setup * Replace justfile with Makefile --- .gitignore | 1 - CHANGELOG.md | 1 + Makefile | 42 ++++ auditlog/locale/ja/LC_MESSAGES/django.mo | Bin 0 -> 2701 bytes auditlog/locale/ja/LC_MESSAGES/django.po | 192 ++++++++++++++++++ auditlog/locale/ko/LC_MESSAGES/django.mo | Bin 0 -> 2466 bytes auditlog/locale/ko/LC_MESSAGES/django.po | 192 ++++++++++++++++++ auditlog/render.py | 5 +- auditlog/templates/auditlog/entry_detail.html | 2 +- 9 files changed, 431 insertions(+), 4 deletions(-) create mode 100644 Makefile create mode 100644 auditlog/locale/ja/LC_MESSAGES/django.mo create mode 100644 auditlog/locale/ja/LC_MESSAGES/django.po create mode 100644 auditlog/locale/ko/LC_MESSAGES/django.mo create mode 100644 auditlog/locale/ko/LC_MESSAGES/django.po 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 0000000000000000000000000000000000000000..42c502075ddb68fac41c8cef493fc22f6a6161d1 GIT binary patch literal 2701 zcma)+U2GIp6vwZKsH>vj2P%pe1QlWG0&2<>47E_GQfWxrghU^vJD2UWvpbuaS+GHp z&J4j8s;Gn_Rzrcc5Q+j~q{v4NzDRs&;+ruf=C-AM@#8_CeDHr}rrqTu(VIQ0{mqP9%w(@&e$~Y3V1g-1l|Y!4BiQjfy=-@!0F)p zsnPf{@B#Q0G1r54z<&$GkELQZgZIPFfHZ$Q_%QfBNbw8cz2FJ(F7O;k`(Fkr#UYT^ z{RHC2hGUL{l=p8j{{m_Mf53adDR5}t=^*7j2c&%zkk+X&D?!?C9Z2gMLDHcqrWtb! zNO`w|6t5ek`JaOn?=(p324ep*xE5Mp0V$6eD3y>l)5m06qhI4)#0@WwNJXkTcYe&XC%y2y8Yu z7xo~G?giCObv*^6HXnv7#vX^^Xv0QzEP|2#)aJn`_h(`B&2bo=Io)Tfg&N)I(ne=b z?Ge~w7-{w-Y_e6RT*Jz;%Dis4+_cigp2`_$_TdJN~cTTzd>nW3Qxt^VF0Ofk#%iDIDE5}&+27tLsN@LxenVH=j1dSIXI4$ zw^L%W!|U5}g4J8BA*WM*#WWfmVY5aA~){RPN}ebriVo{O1Uk3B5_ty#ALsXSz#TdXxz)SPV`7Z;UGvT zI(kWVXlFsTrHDh`LJA8XIrF_Qll%4!gf%E%Jmi;x}|7z zCy-KNqo5E+|y{%;(+zrcC`6irCx^*iy;hX5OA|qC{O^%i6V#_O7luhR$o>DKgjLKir za#|LZ@)a%GD$^*l>~wLC%3HKVZB6Z(VhKyil8LHFc7+aD<*s6dbR(Nqd3lrJChE+*t(i)-WoI0fXLF(JtXxvTqwdONMS@BH zu=Gz#|Evs-%HRyU)^lX^;CUH*A^lU=Ilo`{WP!s*3Z0`pUo+|LmEM=ve<;#JfD_U` zBZCVv=#>6PGT0~mBQofh{-E>@NPoBV`lW~6PRii83y*<+FmEJz-?YQy# z8@~AIXvYVm10B*EWD_5s^tz&PTH|*~?@}1(VCjUB&RydN3O7!eND4XjBh9#?kpp4@m!Ls8q-BK>JwVQ8wOvcz6)+vE08%Ir`_Wb4X>sAcOrA zHC=)(VNE5f;q*qkPu*NVUl{NkJlWWxW7i7bPTq|3;XXH9lZk6HNDE3G862WWK|dQC zyg0I}R|fBfkq6_X!uL3MTrm_dfVGeWzn$T8N3n+Jcv*Caw~+Av4&9_C{?c!jxbF2^ z43ZFgE`=Hghr_Bms8rl-y62JjlP`kLq8W&D7B9n!df`N8(n~|=Q>pt|bO`KU8TXU2 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..6a57ad9500b406bca987260ae88a192369e83d72 GIT binary patch literal 2466 zcmaKsU2GIp6vwZKU#n6@#ZU061wov;l+?f$4A752pwf^6;X#w>&Sg7vc4sp)3vQI8 zz?L*EY6-0r+QlUlu%=BGs8SQ)fhTw)@kv9B4?gJ59reXGWBi}lyWJv*H+$xH&;2^* z{O`>Ev~d0~Lwg(c3ha{y7;6OY&cg%k)`N^a4BiGG1Mh%OfGF&z=z;}0OH5uid|q0{3J;04}#BupMiA#IQRrO2|fyb2U7f-AfI0n+;OAf0y!q)Xn&L9rBP5%@G%tNeGt7WmB|o!bx6Ij6vvz;lW> zK#KDN7y*95zXX@3oZ5f>>g25EgQNIEot_^~F%HZ{ImQ7F1<{t!s_IRMhR zqafrB8`bfm>JwJLNE=$W6t)bu9QGCr>9IFpkSmN&cS!B|GC~bsL!WA-JSpZ2uvcN! zYGHWALd|fsVS5(Om9Qmcm%R?syYeQCzBvw~JE!-V-a%@l#oR`BPi+y5zSV`W#jx47 zG47dmifzmorpGNiQSHU}QkOM#>8XSOEi>NDJ)7?}El)VSGs_(jw;jXf2TZSvdtIi> z;XA_8=|FB8tZ9zBrI|H3LiYrQ%j6$BBJIT7gREIt0u$ANR#R98Yjx}-+iWKV+h!*c zbLZcuG_ZunRdyIa>RhBe#}qE#YiCkM9oycyPsBZ!?NB+nU1l1NYiFFenC+lwUQCovv`$E>j#}dOR*%7YMZiY=<1%;UcM<7Sj!=hpKgp;|!gZ^WsTt(i4Kk z?UaYyxJQxVVfoAmi)NH^Hhdy+Hm8W$ewNu`9i*u0rMoLVl2EufBov)FB58YqBOQ#o z6*p}=9&@v+AiU^C?dv8C@HVMz-m$r*$}b z%Eh~3J2Aco_mk*8fFOJiJ=P_~hV1NEogwzWhfS$O2I6TQLQlr{RvWUV__}U;gJss) zPNKRZ#=GrE+m^PLY5}Y2qLHSu=o(#bjC6YaCa2>vwjvYHO#(9BYE%DJJvN{N8T_8@XTTlh=p*6J!4QYdjdv1-YVs@lY^4 z;upsKiD@1jn)WYD!I!xid3xx-Uzt3Tm$^&+;8(2jeId;9Qn6Us?mw$EQ}oZ~s~dP_Ls%M@Cy&bfF;*4=RZRI`Psn_Z%i@g?eIwzL{%`z& zK7U}m4qD~q, 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 @@