Add internationalization support and update translations
- Updated Wagtail hooks to use gettext for translatable strings in the NewsletterCampaignViewSet and related help texts. - Added LOCALE_PATHS to settings for loading translation files. - Updated base.html to include favicon links. - Translated various strings in the wagtailsettings edit template to support internationalization. - Created and populated zh_Hant translation files for Django messages. - Added a favicon.ico file to the static directory.
This commit is contained in:
parent
7a632c5ebd
commit
5048f865f2
@ -1,5 +1,6 @@
|
||||
from django import forms as django_forms
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from wagtail.admin.panels import (
|
||||
FieldPanel,
|
||||
MultiFieldPanel,
|
||||
@ -39,7 +40,8 @@ class HeaderSettings(BaseGenericSetting):
|
||||
blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="+",
|
||||
help_text="深色底用(亮色 logo)",
|
||||
help_text=_("Use on dark background (light logo)."),
|
||||
verbose_name=_("Light Logo"),
|
||||
)
|
||||
logo_dark = models.ForeignKey(
|
||||
"wagtailimages.Image",
|
||||
@ -47,15 +49,16 @@ class HeaderSettings(BaseGenericSetting):
|
||||
blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="+",
|
||||
help_text="淺色底用(深色 logo)",
|
||||
help_text=_("Use on light background (dark logo)."),
|
||||
verbose_name=_("Dark Logo"),
|
||||
)
|
||||
site_name = models.CharField(max_length=255, blank=True)
|
||||
site_name = models.CharField(max_length=255, blank=True, verbose_name=_("Site Name"))
|
||||
extra_links = StreamField([
|
||||
("link", blocks.StructBlock([
|
||||
("label", blocks.CharBlock()),
|
||||
("url", blocks.URLBlock())
|
||||
("label", blocks.CharBlock(label=_("Label"))),
|
||||
("url", blocks.URLBlock(label=_("URL")))
|
||||
]))
|
||||
], use_json_field=True, blank=True, null=True)
|
||||
], use_json_field=True, blank=True, null=True, verbose_name=_("Extra Links"))
|
||||
|
||||
panels = [
|
||||
MultiFieldPanel(
|
||||
@ -65,70 +68,74 @@ class HeaderSettings(BaseGenericSetting):
|
||||
FieldPanel("site_name"),
|
||||
FieldPanel("extra_links"),
|
||||
],
|
||||
heading="Header Settings",
|
||||
heading=_("Header Settings"),
|
||||
),
|
||||
]
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Header Settings"
|
||||
verbose_name = _("Header Settings")
|
||||
|
||||
@register_setting
|
||||
class NavigationSettings(BaseGenericSetting):
|
||||
footer_links = StreamField([
|
||||
("section", blocks.StructBlock([
|
||||
("title", blocks.CharBlock(required=False)),
|
||||
("title", blocks.CharBlock(required=False, label=_("Section Title"))),
|
||||
("links", blocks.ListBlock(blocks.StructBlock([
|
||||
("label", blocks.CharBlock()),
|
||||
("url", blocks.URLBlock())
|
||||
("label", blocks.CharBlock(label=_("Label"))),
|
||||
("url", blocks.URLBlock(label=_("URL")))
|
||||
]))),
|
||||
]))
|
||||
], use_json_field=True, blank=True, null=True)
|
||||
], use_json_field=True, blank=True, null=True, verbose_name=_("Footer Links"))
|
||||
|
||||
panels = [
|
||||
FieldPanel("footer_links"),
|
||||
]
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Footer Navigation"
|
||||
verbose_name = _("Footer Navigation")
|
||||
|
||||
class SocialLinkBlock(blocks.StructBlock):
|
||||
SOCIAL_MEDIA_CHOICES = [
|
||||
("facebook", "Facebook"),
|
||||
("twitter", "Twitter"),
|
||||
("instagram", "Instagram"),
|
||||
("threads", "Threads"),
|
||||
("linkedin", "LinkedIn"),
|
||||
("youtube", "YouTube"),
|
||||
("facebook", _("Facebook")),
|
||||
("twitter", _("Twitter")),
|
||||
("instagram", _("Instagram")),
|
||||
("threads", _("Threads")),
|
||||
("linkedin", _("LinkedIn")),
|
||||
("youtube", _("YouTube")),
|
||||
]
|
||||
|
||||
platform = blocks.ChoiceBlock(choices=SOCIAL_MEDIA_CHOICES)
|
||||
url = blocks.URLBlock()
|
||||
platform = blocks.ChoiceBlock(choices=SOCIAL_MEDIA_CHOICES, label=_("Platform"))
|
||||
url = blocks.URLBlock(label=_("URL"))
|
||||
|
||||
class Meta:
|
||||
icon = "link"
|
||||
label = "Social Link"
|
||||
label = _("Social Link")
|
||||
|
||||
@register_setting
|
||||
class SocialMediaSettings(BaseGenericSetting):
|
||||
links = StreamField([
|
||||
("link", SocialLinkBlock()),
|
||||
], use_json_field=True)
|
||||
], use_json_field=True, verbose_name=_("Social Links"))
|
||||
|
||||
panels = [FieldPanel("links")]
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Social Media Settings")
|
||||
|
||||
|
||||
@register_setting
|
||||
class MailSmtpSettings(BaseGenericSetting):
|
||||
smtp_relay_host = models.CharField(max_length=255, blank=True)
|
||||
smtp_relay_port = models.PositiveIntegerField(default=587)
|
||||
smtp_use_tls = models.BooleanField(default=True)
|
||||
smtp_relay_host = models.CharField(max_length=255, blank=True, verbose_name=_("SMTP Relay Host"))
|
||||
smtp_relay_port = models.PositiveIntegerField(default=587, verbose_name=_("SMTP Relay Port"))
|
||||
smtp_use_tls = models.BooleanField(default=True, verbose_name=_("Use TLS"))
|
||||
smtp_use_ssl = models.BooleanField(
|
||||
default=False,
|
||||
help_text="465 常用 SSL(Implicit TLS);587 常用 STARTTLS(TLS)。",
|
||||
help_text=_("Port 465 usually uses SSL (Implicit TLS); port 587 usually uses STARTTLS (TLS)."),
|
||||
verbose_name=_("Use SSL"),
|
||||
)
|
||||
smtp_timeout_seconds = models.PositiveIntegerField(default=15)
|
||||
smtp_username = models.CharField(max_length=255, blank=True)
|
||||
smtp_password = models.TextField(blank=True)
|
||||
smtp_timeout_seconds = models.PositiveIntegerField(default=15, verbose_name=_("SMTP Timeout Seconds"))
|
||||
smtp_username = models.CharField(max_length=255, blank=True, verbose_name=_("SMTP Username"))
|
||||
smtp_password = models.TextField(blank=True, verbose_name=_("SMTP Password"))
|
||||
|
||||
panels = [
|
||||
MultiFieldPanel(
|
||||
@ -144,12 +151,12 @@ class MailSmtpSettings(BaseGenericSetting):
|
||||
widget=django_forms.PasswordInput(render_value=False),
|
||||
),
|
||||
],
|
||||
heading="SMTP Settings",
|
||||
heading=_("SMTP Settings"),
|
||||
),
|
||||
]
|
||||
|
||||
class Meta:
|
||||
verbose_name = "SMTP Settings"
|
||||
verbose_name = _("SMTP Settings")
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.smtp_use_tls and self.smtp_use_ssl:
|
||||
@ -166,83 +173,95 @@ class MailSmtpSettings(BaseGenericSetting):
|
||||
|
||||
@register_setting
|
||||
class NewsletterSystemSettings(BaseGenericSetting):
|
||||
member_center_base_url = models.URLField(blank=True)
|
||||
member_center_base_url = models.URLField(blank=True, verbose_name=_("Member Center Base URL"))
|
||||
member_center_subscribe_path = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
default="/newsletter/subscribe",
|
||||
verbose_name=_("Subscribe Path"),
|
||||
)
|
||||
member_center_confirm_path = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
default="/newsletter/confirm",
|
||||
verbose_name=_("Confirm Path"),
|
||||
)
|
||||
member_center_unsubscribe_token_path = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
default="/newsletter/unsubscribe-token",
|
||||
verbose_name=_("Unsubscribe Token Path"),
|
||||
)
|
||||
member_center_unsubscribe_path = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
default="/newsletter/unsubscribe",
|
||||
verbose_name=_("Unsubscribe Path"),
|
||||
)
|
||||
member_center_subscriptions_path = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
default="/newsletter/subscriptions",
|
||||
verbose_name=_("Subscriptions Path"),
|
||||
)
|
||||
member_center_oauth_token_path = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
default="/oauth/token",
|
||||
verbose_name=_("OAuth Token Path"),
|
||||
)
|
||||
member_center_oauth_client_id = models.CharField(max_length=255, blank=True)
|
||||
member_center_oauth_client_secret = models.TextField(blank=True)
|
||||
member_center_oauth_client_id = models.CharField(max_length=255, blank=True, verbose_name=_("OAuth Client ID"))
|
||||
member_center_oauth_client_secret = models.TextField(blank=True, verbose_name=_("OAuth Client Secret"))
|
||||
member_center_oauth_scope = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
default="newsletter:list.read",
|
||||
verbose_name=_("OAuth Scope"),
|
||||
)
|
||||
member_center_oauth_audience = models.CharField(max_length=255, blank=True)
|
||||
member_center_oauth_audience = models.CharField(max_length=255, blank=True, verbose_name=_("OAuth Audience"))
|
||||
member_center_one_click_unsubscribe_path = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
default="/api/subscriptions/unsubscribe",
|
||||
verbose_name=_("One-Click Unsubscribe Path"),
|
||||
)
|
||||
member_center_tenant_id = models.CharField(max_length=128, blank=True)
|
||||
member_center_list_id = models.CharField(max_length=128, blank=True)
|
||||
member_center_timeout_seconds = models.PositiveIntegerField(default=10)
|
||||
member_center_tenant_id = models.CharField(max_length=128, blank=True, verbose_name=_("Tenant ID"))
|
||||
member_center_list_id = models.CharField(max_length=128, blank=True, verbose_name=_("List ID"))
|
||||
member_center_timeout_seconds = models.PositiveIntegerField(default=10, verbose_name=_("Member Center Timeout Seconds"))
|
||||
|
||||
send_engine_base_url = models.URLField(blank=True)
|
||||
send_engine_base_url = models.URLField(blank=True, verbose_name=_("Send Engine Base URL"))
|
||||
send_engine_send_jobs_path = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
default="/api/send-jobs",
|
||||
verbose_name=_("Send Jobs Path"),
|
||||
)
|
||||
send_engine_oauth_scope = models.CharField(max_length=255, blank=True)
|
||||
send_engine_timeout_seconds = models.PositiveIntegerField(default=10)
|
||||
send_engine_retry_interval_seconds = models.PositiveIntegerField(default=300)
|
||||
send_engine_retry_max_attempts = models.PositiveIntegerField(default=3)
|
||||
send_engine_oauth_scope = models.CharField(max_length=255, blank=True, verbose_name=_("Send Engine OAuth Scope"))
|
||||
send_engine_timeout_seconds = models.PositiveIntegerField(default=10, verbose_name=_("Send Engine Timeout Seconds"))
|
||||
send_engine_retry_interval_seconds = models.PositiveIntegerField(default=300, verbose_name=_("Retry Interval Seconds"))
|
||||
send_engine_retry_max_attempts = models.PositiveIntegerField(default=3, verbose_name=_("Retry Max Attempts"))
|
||||
|
||||
sender_name = models.CharField(max_length=255, blank=True)
|
||||
sender_email = models.EmailField(blank=True)
|
||||
reply_to_email = models.EmailField(blank=True)
|
||||
default_charset = models.CharField(max_length=50, default="utf-8")
|
||||
sender_name = models.CharField(max_length=255, blank=True, verbose_name=_("Sender Name"))
|
||||
sender_email = models.EmailField(blank=True, verbose_name=_("Sender Email"))
|
||||
reply_to_email = models.EmailField(blank=True, verbose_name=_("Reply-To Email"))
|
||||
default_charset = models.CharField(max_length=50, default="utf-8", verbose_name=_("Default Charset"))
|
||||
one_click_endpoint_path = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
default="/u/unsubscribe",
|
||||
verbose_name=_("One-Click Endpoint Path"),
|
||||
)
|
||||
one_click_token_secret = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
help_text="One-click token 簽章 secret;留空則使用 Django SECRET_KEY。",
|
||||
help_text=_("One-click token signing secret. Leave blank to use Django SECRET_KEY."),
|
||||
verbose_name=_("One-Click Token Secret"),
|
||||
)
|
||||
one_click_token_ttl_seconds = models.PositiveIntegerField(default=60 * 60 * 24 * 30)
|
||||
one_click_token_ttl_seconds = models.PositiveIntegerField(default=60 * 60 * 24 * 30, verbose_name=_("One-Click Token TTL Seconds"))
|
||||
site_base_url = models.URLField(
|
||||
blank=True,
|
||||
help_text="排程發送使用的站台網址(例如 https://news.example.com)。",
|
||||
help_text=_("Site base URL for scheduler sends, e.g. https://news.example.com"),
|
||||
verbose_name=_("Site Base URL"),
|
||||
)
|
||||
|
||||
panels = [
|
||||
@ -267,7 +286,7 @@ class NewsletterSystemSettings(BaseGenericSetting):
|
||||
FieldPanel("member_center_list_id"),
|
||||
FieldPanel("member_center_timeout_seconds"),
|
||||
],
|
||||
heading="Member Center",
|
||||
heading=_("Member Center"),
|
||||
),
|
||||
MultiFieldPanel(
|
||||
[
|
||||
@ -278,7 +297,7 @@ class NewsletterSystemSettings(BaseGenericSetting):
|
||||
FieldPanel("send_engine_retry_interval_seconds"),
|
||||
FieldPanel("send_engine_retry_max_attempts"),
|
||||
],
|
||||
heading="Send Engine",
|
||||
heading=_("Send Engine"),
|
||||
),
|
||||
MultiFieldPanel(
|
||||
[
|
||||
@ -287,7 +306,7 @@ class NewsletterSystemSettings(BaseGenericSetting):
|
||||
FieldPanel("one_click_token_ttl_seconds"),
|
||||
FieldPanel("site_base_url"),
|
||||
],
|
||||
heading="List-Unsubscribe One-Click",
|
||||
heading=_("List-Unsubscribe One-Click"),
|
||||
),
|
||||
MultiFieldPanel(
|
||||
[
|
||||
@ -296,12 +315,12 @@ class NewsletterSystemSettings(BaseGenericSetting):
|
||||
FieldPanel("reply_to_email"),
|
||||
FieldPanel("default_charset"),
|
||||
],
|
||||
heading="Newsletter Mail",
|
||||
heading=_("Newsletter Mail"),
|
||||
),
|
||||
]
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Newsletter System Settings"
|
||||
verbose_name = _("Newsletter System Settings")
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
if self.pk and not self.member_center_oauth_client_secret:
|
||||
@ -315,18 +334,20 @@ class NewsletterSystemSettings(BaseGenericSetting):
|
||||
|
||||
@register_setting
|
||||
class SystemNotificationMailSettings(BaseGenericSetting):
|
||||
contact_form_from_name = models.CharField(max_length=255, blank=True)
|
||||
contact_form_from_email = models.EmailField(blank=True)
|
||||
contact_form_reply_to_email = models.EmailField(blank=True)
|
||||
contact_form_from_name = models.CharField(max_length=255, blank=True, verbose_name=_("Contact Form Sender Name"))
|
||||
contact_form_from_email = models.EmailField(blank=True, verbose_name=_("Contact Form Sender Email"))
|
||||
contact_form_reply_to_email = models.EmailField(blank=True, verbose_name=_("Contact Form Reply-To Email"))
|
||||
contact_form_to_emails = models.TextField(
|
||||
blank=True,
|
||||
help_text="可填多個收件人,以逗號或換行分隔。",
|
||||
help_text=_("Multiple recipients separated by comma or newline."),
|
||||
verbose_name=_("Contact Form Notification Recipients"),
|
||||
)
|
||||
contact_form_subject_prefix = models.CharField(max_length=255, blank=True, default="[Contact Us]")
|
||||
contact_form_subject_prefix = models.CharField(max_length=255, blank=True, default="[Contact Us]", verbose_name=_("Contact Form Subject Prefix"))
|
||||
contact_form_user_subject_template = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
default="已收到您的聯絡表單",
|
||||
verbose_name=_("User Copy Subject Template"),
|
||||
)
|
||||
contact_form_user_text_template = models.TextField(
|
||||
blank=True,
|
||||
@ -338,6 +359,7 @@ class SystemNotificationMailSettings(BaseGenericSetting):
|
||||
"問題類別: {{category}}\n\n"
|
||||
"留言內容:\n{{message}}\n"
|
||||
),
|
||||
verbose_name=_("User Copy Text Template"),
|
||||
)
|
||||
contact_form_user_html_template = models.TextField(
|
||||
blank=True,
|
||||
@ -352,8 +374,9 @@ class SystemNotificationMailSettings(BaseGenericSetting):
|
||||
"<p>留言內容:</p>"
|
||||
"<p>{{message}}</p>"
|
||||
),
|
||||
verbose_name=_("User Copy HTML Template"),
|
||||
)
|
||||
default_charset = models.CharField(max_length=50, default="utf-8")
|
||||
default_charset = models.CharField(max_length=50, default="utf-8", verbose_name=_("Default Charset"))
|
||||
|
||||
panels = [
|
||||
MultiFieldPanel(
|
||||
@ -368,12 +391,12 @@ class SystemNotificationMailSettings(BaseGenericSetting):
|
||||
FieldPanel("contact_form_user_html_template"),
|
||||
FieldPanel("default_charset"),
|
||||
],
|
||||
heading="Contact Us Notification Mail",
|
||||
heading=_("Contact Us Notification Mail"),
|
||||
),
|
||||
]
|
||||
|
||||
class Meta:
|
||||
verbose_name = "System Notification Mail Settings"
|
||||
verbose_name = _("System Notification Mail Settings")
|
||||
|
||||
def contact_form_recipient_list(self) -> list[str]:
|
||||
raw = (self.contact_form_to_emails or "").replace("\n", ",")
|
||||
@ -404,24 +427,24 @@ class NewsletterCampaign(models.Model):
|
||||
STATUS_SENT = "sent"
|
||||
STATUS_FAILED = "failed"
|
||||
STATUS_CHOICES = [
|
||||
(STATUS_DRAFT, "Draft"),
|
||||
(STATUS_SCHEDULED, "Scheduled"),
|
||||
(STATUS_SENDING, "Sending"),
|
||||
(STATUS_SENT, "Sent"),
|
||||
(STATUS_FAILED, "Failed"),
|
||||
(STATUS_DRAFT, _("Draft")),
|
||||
(STATUS_SCHEDULED, _("Scheduled")),
|
||||
(STATUS_SENDING, _("Sending")),
|
||||
(STATUS_SENT, _("Sent")),
|
||||
(STATUS_FAILED, _("Failed")),
|
||||
]
|
||||
|
||||
title = models.CharField(max_length=255)
|
||||
list_id = models.CharField(max_length=128, blank=True)
|
||||
subject_template = models.CharField(max_length=255)
|
||||
html_template = models.TextField()
|
||||
text_template = models.TextField(blank=True)
|
||||
status = models.CharField(max_length=16, choices=STATUS_CHOICES, default=STATUS_DRAFT)
|
||||
scheduled_at = models.DateTimeField(null=True, blank=True)
|
||||
sent_at = models.DateTimeField(null=True, blank=True)
|
||||
last_error = models.TextField(blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
title = models.CharField(max_length=255, verbose_name=_("Title"))
|
||||
list_id = models.CharField(max_length=128, blank=True, verbose_name=_("List ID"))
|
||||
subject_template = models.CharField(max_length=255, verbose_name=_("Subject Template"))
|
||||
html_template = models.TextField(verbose_name=_("HTML Template"))
|
||||
text_template = models.TextField(blank=True, verbose_name=_("Text Template"))
|
||||
status = models.CharField(max_length=16, choices=STATUS_CHOICES, default=STATUS_DRAFT, verbose_name=_("Status"))
|
||||
scheduled_at = models.DateTimeField(null=True, blank=True, verbose_name=_("Scheduled At"))
|
||||
sent_at = models.DateTimeField(null=True, blank=True, verbose_name=_("Sent At"))
|
||||
last_error = models.TextField(blank=True, verbose_name=_("Last Error"))
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
|
||||
|
||||
panels = [
|
||||
FieldPanel("title"),
|
||||
@ -434,6 +457,8 @@ class NewsletterCampaign(models.Model):
|
||||
|
||||
class Meta:
|
||||
ordering = ["-created_at"]
|
||||
verbose_name = _("Newsletter Campaign")
|
||||
verbose_name_plural = _("Newsletter Campaigns")
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
@ -451,16 +476,17 @@ class NewsletterDispatchRecord(models.Model):
|
||||
NewsletterCampaign,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="dispatch_records",
|
||||
verbose_name=_("Campaign"),
|
||||
)
|
||||
subscriber_id = models.CharField(max_length=128, blank=True)
|
||||
email = models.EmailField(blank=True)
|
||||
status = models.CharField(max_length=32, blank=True)
|
||||
retry_count = models.PositiveIntegerField(default=0)
|
||||
next_retry_at = models.DateTimeField(null=True, blank=True)
|
||||
response_status = models.PositiveIntegerField(null=True, blank=True)
|
||||
response_payload = models.JSONField(default=dict, blank=True)
|
||||
error_message = models.TextField(blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
subscriber_id = models.CharField(max_length=128, blank=True, verbose_name=_("Subscriber ID"))
|
||||
email = models.EmailField(blank=True, verbose_name=_("Email"))
|
||||
status = models.CharField(max_length=32, blank=True, verbose_name=_("Status"))
|
||||
retry_count = models.PositiveIntegerField(default=0, verbose_name=_("Retry Count"))
|
||||
next_retry_at = models.DateTimeField(null=True, blank=True, verbose_name=_("Next Retry At"))
|
||||
response_status = models.PositiveIntegerField(null=True, blank=True, verbose_name=_("Response Status"))
|
||||
response_payload = models.JSONField(default=dict, blank=True, verbose_name=_("Response Payload"))
|
||||
error_message = models.TextField(blank=True, verbose_name=_("Error Message"))
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
|
||||
|
||||
panels = [
|
||||
FieldPanel("campaign"),
|
||||
@ -476,6 +502,8 @@ class NewsletterDispatchRecord(models.Model):
|
||||
|
||||
class Meta:
|
||||
ordering = ["-created_at"]
|
||||
verbose_name = _("Newsletter Dispatch Record")
|
||||
verbose_name_plural = _("Newsletter Dispatch Records")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.campaign_id}:{self.email or self.subscriber_id}"
|
||||
@ -486,35 +514,43 @@ class NewsletterTemplateSettings(BaseGenericSetting):
|
||||
subscribe_subject_template = models.CharField(
|
||||
max_length=255,
|
||||
default="請確認您的電子報訂閱",
|
||||
verbose_name=_("Subscribe Subject Template"),
|
||||
)
|
||||
subscribe_html_template = models.TextField(
|
||||
default=(
|
||||
"<p>您好,請點擊以下連結完成訂閱:</p>"
|
||||
"<p><a href='{{confirm_url}}'>{{confirm_url}}</a></p>"
|
||||
),
|
||||
verbose_name=_("Subscribe HTML Template"),
|
||||
)
|
||||
subscribe_text_template = models.TextField(
|
||||
default="您好,請點擊以下連結完成訂閱:{{confirm_url}}",
|
||||
verbose_name=_("Subscribe Text Template"),
|
||||
)
|
||||
confirm_success_template = RichTextField(
|
||||
blank=True,
|
||||
default="<p>訂閱確認成功。</p>",
|
||||
verbose_name=_("Confirm Success Template"),
|
||||
)
|
||||
confirm_failure_template = RichTextField(
|
||||
blank=True,
|
||||
default="<p>訂閱確認失敗,請稍後再試。</p>",
|
||||
verbose_name=_("Confirm Failure Template"),
|
||||
)
|
||||
unsubscribe_intro_template = RichTextField(
|
||||
blank=True,
|
||||
default="<p>確認要退訂電子報嗎?</p>",
|
||||
verbose_name=_("Unsubscribe Intro Template"),
|
||||
)
|
||||
unsubscribe_success_template = RichTextField(
|
||||
blank=True,
|
||||
default="<p>已完成退訂。</p>",
|
||||
verbose_name=_("Unsubscribe Success Template"),
|
||||
)
|
||||
unsubscribe_failure_template = RichTextField(
|
||||
blank=True,
|
||||
default="<p>退訂失敗,請稍後再試。</p>",
|
||||
verbose_name=_("Unsubscribe Failure Template"),
|
||||
)
|
||||
|
||||
panels = [
|
||||
@ -524,7 +560,7 @@ class NewsletterTemplateSettings(BaseGenericSetting):
|
||||
FieldPanel("subscribe_html_template"),
|
||||
FieldPanel("subscribe_text_template"),
|
||||
],
|
||||
heading="Subscribe Confirmation Email",
|
||||
heading=_("Subscribe Confirmation Email"),
|
||||
),
|
||||
MultiFieldPanel(
|
||||
[
|
||||
@ -534,18 +570,18 @@ class NewsletterTemplateSettings(BaseGenericSetting):
|
||||
FieldPanel("unsubscribe_success_template"),
|
||||
FieldPanel("unsubscribe_failure_template"),
|
||||
],
|
||||
heading="Page Templates",
|
||||
heading=_("Page Templates"),
|
||||
),
|
||||
]
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Newsletter Template Settings"
|
||||
verbose_name = _("Newsletter Template Settings")
|
||||
|
||||
|
||||
@register_snippet
|
||||
class BannerSnippet(models.Model):
|
||||
key = models.CharField(max_length=50, blank=True, help_text="識別用 key(例如 home / category)")
|
||||
title = models.CharField(max_length=255, blank=True)
|
||||
key = models.CharField(max_length=50, blank=True, help_text=_("Identifier key, e.g. home / category"), verbose_name=_("Key"))
|
||||
title = models.CharField(max_length=255, blank=True, verbose_name=_("Title"))
|
||||
image = models.ForeignKey(
|
||||
"wagtailimages.Image",
|
||||
null=True,
|
||||
@ -554,9 +590,9 @@ class BannerSnippet(models.Model):
|
||||
related_name="+",
|
||||
)
|
||||
link_url = models.URLField(blank=True)
|
||||
link_text = models.CharField(max_length=100, blank=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
sort_order = models.PositiveIntegerField(default=0)
|
||||
link_text = models.CharField(max_length=100, blank=True, verbose_name=_("Link Text"))
|
||||
is_active = models.BooleanField(default=True, verbose_name=_("Active"))
|
||||
sort_order = models.PositiveIntegerField(default=0, verbose_name=_("Sort Order"))
|
||||
|
||||
panels = [
|
||||
FieldPanel("key"),
|
||||
@ -570,8 +606,8 @@ class BannerSnippet(models.Model):
|
||||
|
||||
class Meta:
|
||||
ordering = ["sort_order", "id"]
|
||||
verbose_name = "Banner"
|
||||
verbose_name_plural = "Banners"
|
||||
verbose_name = _("Banner")
|
||||
verbose_name_plural = _("Banners")
|
||||
|
||||
def __str__(self):
|
||||
return self.title or f"Banner {self.pk}"
|
||||
@ -602,7 +638,7 @@ class FooterText(
|
||||
return {"footer_text": self.body}
|
||||
|
||||
class Meta(TranslatableMixin.Meta):
|
||||
verbose_name_plural = "Footer Text"
|
||||
verbose_name_plural = _("Footer Text")
|
||||
|
||||
|
||||
class ContactFormSubmission(models.Model):
|
||||
@ -611,24 +647,26 @@ class ContactFormSubmission(models.Model):
|
||||
CATEGORY_CAREER = "career"
|
||||
CATEGORY_OTHER = "other"
|
||||
CATEGORY_CHOICES = [
|
||||
(CATEGORY_COLLABORATION, "合作邀約"),
|
||||
(CATEGORY_WEBSITE_ISSUE, "網站問題回報"),
|
||||
(CATEGORY_CAREER, "求職專區"),
|
||||
(CATEGORY_OTHER, "其他"),
|
||||
(CATEGORY_COLLABORATION, _("Collaboration")),
|
||||
(CATEGORY_WEBSITE_ISSUE, _("Website Issue")),
|
||||
(CATEGORY_CAREER, _("Career")),
|
||||
(CATEGORY_OTHER, _("Other")),
|
||||
]
|
||||
|
||||
name = models.CharField(max_length=100)
|
||||
email = models.EmailField(blank=True)
|
||||
contact = models.CharField(max_length=255)
|
||||
category = models.CharField(max_length=32, choices=CATEGORY_CHOICES)
|
||||
message = models.TextField()
|
||||
source_page = models.CharField(max_length=512, blank=True)
|
||||
ip_address = models.GenericIPAddressField(null=True, blank=True)
|
||||
user_agent = models.TextField(blank=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
name = models.CharField(max_length=100, verbose_name=_("Name"))
|
||||
email = models.EmailField(blank=True, verbose_name=_("Email"))
|
||||
contact = models.CharField(max_length=255, verbose_name=_("Contact"))
|
||||
category = models.CharField(max_length=32, choices=CATEGORY_CHOICES, verbose_name=_("Category"))
|
||||
message = models.TextField(verbose_name=_("Message"))
|
||||
source_page = models.CharField(max_length=512, blank=True, verbose_name=_("Source Page"))
|
||||
ip_address = models.GenericIPAddressField(null=True, blank=True, verbose_name=_("IP Address"))
|
||||
user_agent = models.TextField(blank=True, verbose_name=_("User Agent"))
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
|
||||
|
||||
class Meta:
|
||||
ordering = ["-created_at"]
|
||||
verbose_name = _("Contact Form Submission")
|
||||
verbose_name_plural = _("Contact Form Submissions")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.get_category_display()} - {self.name}"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import get_language
|
||||
from django.utils.translation import gettext as _
|
||||
from wagtail import hooks
|
||||
from wagtail.admin.panels import FieldPanel
|
||||
from wagtail.admin.rich_text import DraftailRichTextArea
|
||||
@ -25,7 +25,7 @@ class NewsletterCampaignCreateView(CreateView):
|
||||
class NewsletterCampaignViewSet(SnippetViewSet):
|
||||
model = NewsletterCampaign
|
||||
icon = "mail"
|
||||
menu_label = "Newsletter campaigns"
|
||||
menu_label = _("Newsletter campaigns")
|
||||
menu_order = 250
|
||||
add_to_admin_menu = True
|
||||
add_view_class = NewsletterCampaignCreateView
|
||||
@ -36,7 +36,7 @@ class NewsletterCampaignViewSet(SnippetViewSet):
|
||||
FieldPanel("title"),
|
||||
FieldPanel(
|
||||
"list_id",
|
||||
help_text="留空時會使用 Newsletter System Settings 的 member_center_list_id。",
|
||||
help_text=_("Leave blank to use member_center_list_id from Newsletter System Settings."),
|
||||
),
|
||||
FieldPanel("subject_template"),
|
||||
FieldPanel(
|
||||
@ -44,7 +44,7 @@ class NewsletterCampaignViewSet(SnippetViewSet):
|
||||
widget=DraftailRichTextArea(
|
||||
features=["h2", "h3", "bold", "italic", "link", "image", "ul", "ol"],
|
||||
),
|
||||
help_text="可用圖片按鈕從 Wagtail 圖庫選圖或上傳。建議使用圖片 URL,不要在 CMS 端轉 Base64。",
|
||||
help_text=_("Use image picker/upload from Wagtail. Prefer image URLs instead of Base64 in CMS."),
|
||||
),
|
||||
FieldPanel("text_template"),
|
||||
FieldPanel("scheduled_at"),
|
||||
@ -61,7 +61,7 @@ class ReadOnlySnippetPermissionPolicy(ModelPermissionPolicy):
|
||||
class NewsletterDispatchRecordViewSet(SnippetViewSet):
|
||||
model = NewsletterDispatchRecord
|
||||
icon = "tasks"
|
||||
menu_label = "Newsletter dispatch records"
|
||||
menu_label = _("Newsletter dispatch records")
|
||||
menu_order = 251
|
||||
add_to_admin_menu = True
|
||||
inspect_view_enabled = True
|
||||
@ -94,8 +94,7 @@ def newsletter_campaign_listing_buttons(snippet, user, next_url=None):
|
||||
if snippet.status == NewsletterCampaign.STATUS_SENDING:
|
||||
return
|
||||
|
||||
language = (get_language() or "").lower()
|
||||
label = "馬上發送" if language.startswith("zh") else "Send now"
|
||||
label = _("Send now")
|
||||
yield Button(
|
||||
label,
|
||||
reverse("newsletter_campaign_send_now", args=[snippet.pk]),
|
||||
|
||||
BIN
innovedus_cms/locale/zh_Hant/LC_MESSAGES/django.mo
Normal file
BIN
innovedus_cms/locale/zh_Hant/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
432
innovedus_cms/locale/zh_Hant/LC_MESSAGES/django.po
Normal file
432
innovedus_cms/locale/zh_Hant/LC_MESSAGES/django.po
Normal file
@ -0,0 +1,432 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: innovedus_cms\n"
|
||||
"POT-Creation-Date: 2026-03-12 00:00+0000\n"
|
||||
"PO-Revision-Date: 2026-03-12 00:00+0000\n"
|
||||
"Last-Translator: Codex\n"
|
||||
"Language-Team: zh_Hant\n"
|
||||
"Language: zh_Hant\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"
|
||||
|
||||
msgid "Header Settings"
|
||||
msgstr "頁首設定"
|
||||
|
||||
msgid "Use on dark background (light logo)."
|
||||
msgstr "深色底用(亮色 logo)"
|
||||
|
||||
msgid "Light Logo"
|
||||
msgstr "亮色 Logo"
|
||||
|
||||
msgid "Use on light background (dark logo)."
|
||||
msgstr "淺色底用(深色 logo)"
|
||||
|
||||
msgid "Dark Logo"
|
||||
msgstr "深色 Logo"
|
||||
|
||||
msgid "Site Name"
|
||||
msgstr "網站名稱"
|
||||
|
||||
msgid "Extra Links"
|
||||
msgstr "額外連結"
|
||||
|
||||
msgid "Label"
|
||||
msgstr "標籤"
|
||||
|
||||
msgid "URL"
|
||||
msgstr "連結"
|
||||
|
||||
msgid "Footer Navigation"
|
||||
msgstr "頁尾導覽"
|
||||
|
||||
msgid "Footer Links"
|
||||
msgstr "頁尾連結"
|
||||
|
||||
msgid "Section Title"
|
||||
msgstr "區塊標題"
|
||||
|
||||
msgid "Social Link"
|
||||
msgstr "社群連結"
|
||||
|
||||
msgid "Social Links"
|
||||
msgstr "社群連結"
|
||||
|
||||
msgid "Platform"
|
||||
msgstr "平台"
|
||||
|
||||
msgid "SMTP Settings"
|
||||
msgstr "SMTP 設定"
|
||||
|
||||
msgid "SMTP Relay Host"
|
||||
msgstr "SMTP Relay Host"
|
||||
|
||||
msgid "SMTP Relay Port"
|
||||
msgstr "SMTP Relay Port"
|
||||
|
||||
msgid "Use TLS"
|
||||
msgstr "使用 TLS"
|
||||
|
||||
msgid "Use SSL"
|
||||
msgstr "使用 SSL"
|
||||
|
||||
msgid "Port 465 usually uses SSL (Implicit TLS); port 587 usually uses STARTTLS (TLS)."
|
||||
msgstr "465 常用 SSL(Implicit TLS);587 常用 STARTTLS(TLS)。"
|
||||
|
||||
msgid "SMTP Timeout Seconds"
|
||||
msgstr "SMTP 逾時秒數"
|
||||
|
||||
msgid "SMTP Username"
|
||||
msgstr "SMTP 帳號"
|
||||
|
||||
msgid "SMTP Password"
|
||||
msgstr "SMTP 密碼"
|
||||
|
||||
msgid "Newsletter System Settings"
|
||||
msgstr "電子報系統設定"
|
||||
|
||||
msgid "Member Center"
|
||||
msgstr "會員中心"
|
||||
|
||||
msgid "Member Center Base URL"
|
||||
msgstr "會員中心 Base URL"
|
||||
|
||||
msgid "Subscribe Path"
|
||||
msgstr "訂閱路徑"
|
||||
|
||||
msgid "Confirm Path"
|
||||
msgstr "確認路徑"
|
||||
|
||||
msgid "Unsubscribe Token Path"
|
||||
msgstr "退訂 Token 路徑"
|
||||
|
||||
msgid "Unsubscribe Path"
|
||||
msgstr "退訂路徑"
|
||||
|
||||
msgid "Subscriptions Path"
|
||||
msgstr "訂閱列表路徑"
|
||||
|
||||
msgid "OAuth Token Path"
|
||||
msgstr "OAuth Token 路徑"
|
||||
|
||||
msgid "OAuth Client ID"
|
||||
msgstr "OAuth Client ID"
|
||||
|
||||
msgid "OAuth Client Secret"
|
||||
msgstr "OAuth Client Secret"
|
||||
|
||||
msgid "OAuth Scope"
|
||||
msgstr "OAuth Scope"
|
||||
|
||||
msgid "OAuth Audience"
|
||||
msgstr "OAuth Audience"
|
||||
|
||||
msgid "One-Click Unsubscribe Path"
|
||||
msgstr "一鍵退訂路徑"
|
||||
|
||||
msgid "Tenant ID"
|
||||
msgstr "Tenant ID"
|
||||
|
||||
msgid "List ID"
|
||||
msgstr "List ID"
|
||||
|
||||
msgid "Member Center Timeout Seconds"
|
||||
msgstr "會員中心逾時秒數"
|
||||
|
||||
msgid "Send Engine"
|
||||
msgstr "發送引擎"
|
||||
|
||||
msgid "Send Engine Base URL"
|
||||
msgstr "發送引擎 Base URL"
|
||||
|
||||
msgid "Send Jobs Path"
|
||||
msgstr "發送任務路徑"
|
||||
|
||||
msgid "Send Engine OAuth Scope"
|
||||
msgstr "發送引擎 OAuth Scope"
|
||||
|
||||
msgid "Send Engine Timeout Seconds"
|
||||
msgstr "發送引擎逾時秒數"
|
||||
|
||||
msgid "Retry Interval Seconds"
|
||||
msgstr "重試間隔秒數"
|
||||
|
||||
msgid "Retry Max Attempts"
|
||||
msgstr "最大重試次數"
|
||||
|
||||
msgid "Newsletter Mail"
|
||||
msgstr "電子報寄件設定"
|
||||
|
||||
msgid "Sender Name"
|
||||
msgstr "寄件者名稱"
|
||||
|
||||
msgid "Sender Email"
|
||||
msgstr "寄件者 Email"
|
||||
|
||||
msgid "Reply-To Email"
|
||||
msgstr "回覆 Email"
|
||||
|
||||
msgid "Default Charset"
|
||||
msgstr "預設字元編碼"
|
||||
|
||||
msgid "List-Unsubscribe One-Click"
|
||||
msgstr "一鍵退訂設定"
|
||||
|
||||
msgid "One-Click Endpoint Path"
|
||||
msgstr "一鍵退訂端點路徑"
|
||||
|
||||
msgid "One-Click Token Secret"
|
||||
msgstr "一鍵退訂 Token 密鑰"
|
||||
|
||||
msgid "One-click token signing secret. Leave blank to use Django SECRET_KEY."
|
||||
msgstr "One-click token 簽章密鑰;留空則使用 Django SECRET_KEY。"
|
||||
|
||||
msgid "One-Click Token TTL Seconds"
|
||||
msgstr "一鍵退訂 Token 有效秒數"
|
||||
|
||||
msgid "Site Base URL"
|
||||
msgstr "站台 Base URL"
|
||||
|
||||
msgid "Site base URL for scheduler sends, e.g. https://news.example.com"
|
||||
msgstr "排程發送使用的站台網址(例如 https://news.example.com)。"
|
||||
|
||||
msgid "System Notification Mail Settings"
|
||||
msgstr "系統通知信設定"
|
||||
|
||||
msgid "Contact Us Notification Mail"
|
||||
msgstr "聯絡我們通知信"
|
||||
|
||||
msgid "Contact Form Sender Name"
|
||||
msgstr "聯絡表單寄件者名稱"
|
||||
|
||||
msgid "Contact Form Sender Email"
|
||||
msgstr "聯絡表單寄件者 Email"
|
||||
|
||||
msgid "Contact Form Reply-To Email"
|
||||
msgstr "聯絡表單回覆 Email"
|
||||
|
||||
msgid "Contact Form Notification Recipients"
|
||||
msgstr "聯絡表單通知收件人"
|
||||
|
||||
msgid "Multiple recipients separated by comma or newline."
|
||||
msgstr "可填多個收件人,以逗號或換行分隔。"
|
||||
|
||||
msgid "Contact Form Subject Prefix"
|
||||
msgstr "聯絡表單主旨前綴"
|
||||
|
||||
msgid "User Copy Subject Template"
|
||||
msgstr "使用者存檔信主旨模板"
|
||||
|
||||
msgid "User Copy Text Template"
|
||||
msgstr "使用者存檔信純文字模板"
|
||||
|
||||
msgid "User Copy HTML Template"
|
||||
msgstr "使用者存檔信 HTML 模板"
|
||||
|
||||
msgid "Subscribe Confirmation Email"
|
||||
msgstr "訂閱確認信"
|
||||
|
||||
msgid "Page Templates"
|
||||
msgstr "頁面模板"
|
||||
|
||||
msgid "Newsletter Template Settings"
|
||||
msgstr "電子報模板設定"
|
||||
|
||||
msgid "Banner"
|
||||
msgstr "橫幅"
|
||||
|
||||
msgid "Banners"
|
||||
msgstr "橫幅"
|
||||
|
||||
msgid "Key"
|
||||
msgstr "識別鍵"
|
||||
|
||||
msgid "Identifier key, e.g. home / category"
|
||||
msgstr "識別用 key(例如 home / category)"
|
||||
|
||||
msgid "Title"
|
||||
msgstr "標題"
|
||||
|
||||
msgid "Link Text"
|
||||
msgstr "連結文字"
|
||||
|
||||
msgid "Active"
|
||||
msgstr "啟用"
|
||||
|
||||
msgid "Sort Order"
|
||||
msgstr "排序"
|
||||
|
||||
msgid "Footer Text"
|
||||
msgstr "頁尾文字"
|
||||
|
||||
msgid "Draft"
|
||||
msgstr "草稿"
|
||||
|
||||
msgid "Scheduled"
|
||||
msgstr "已排程"
|
||||
|
||||
msgid "Sending"
|
||||
msgstr "寄送中"
|
||||
|
||||
msgid "Sent"
|
||||
msgstr "已送出"
|
||||
|
||||
msgid "Failed"
|
||||
msgstr "失敗"
|
||||
|
||||
msgid "Newsletter Campaign"
|
||||
msgstr "電子報"
|
||||
|
||||
msgid "Newsletter Campaigns"
|
||||
msgstr "電子報"
|
||||
|
||||
msgid "Newsletter Dispatch Record"
|
||||
msgstr "電子報發送紀錄"
|
||||
|
||||
msgid "Newsletter Dispatch Records"
|
||||
msgstr "電子報發送紀錄"
|
||||
|
||||
msgid "Collaboration"
|
||||
msgstr "合作邀約"
|
||||
|
||||
msgid "Website Issue"
|
||||
msgstr "網站問題回報"
|
||||
|
||||
msgid "Career"
|
||||
msgstr "求職專區"
|
||||
|
||||
msgid "Other"
|
||||
msgstr "其他"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "姓名"
|
||||
|
||||
msgid "Email"
|
||||
msgstr "Email"
|
||||
|
||||
msgid "Contact"
|
||||
msgstr "聯絡方式"
|
||||
|
||||
msgid "Category"
|
||||
msgstr "問題類別"
|
||||
|
||||
msgid "Message"
|
||||
msgstr "留言內容"
|
||||
|
||||
msgid "Source Page"
|
||||
msgstr "來源頁面"
|
||||
|
||||
msgid "IP Address"
|
||||
msgstr "IP 位址"
|
||||
|
||||
msgid "User Agent"
|
||||
msgstr "User Agent"
|
||||
|
||||
msgid "Created At"
|
||||
msgstr "建立時間"
|
||||
|
||||
msgid "Contact Form Submission"
|
||||
msgstr "聯絡表單提交"
|
||||
|
||||
msgid "Contact Form Submissions"
|
||||
msgstr "聯絡表單提交"
|
||||
|
||||
msgid "Newsletter campaigns"
|
||||
msgstr "電子報"
|
||||
|
||||
msgid "Newsletter dispatch records"
|
||||
msgstr "電子報發送紀錄"
|
||||
|
||||
msgid "Social Media Settings"
|
||||
msgstr "社群媒體設定"
|
||||
|
||||
msgid "Subject Template"
|
||||
msgstr "主旨模板"
|
||||
|
||||
msgid "HTML Template"
|
||||
msgstr "HTML 模板"
|
||||
|
||||
msgid "Text Template"
|
||||
msgstr "純文字模板"
|
||||
|
||||
msgid "Status"
|
||||
msgstr "狀態"
|
||||
|
||||
msgid "Scheduled At"
|
||||
msgstr "排程時間"
|
||||
|
||||
msgid "Sent At"
|
||||
msgstr "發送時間"
|
||||
|
||||
msgid "Last Error"
|
||||
msgstr "最後錯誤"
|
||||
|
||||
msgid "Updated At"
|
||||
msgstr "更新時間"
|
||||
|
||||
msgid "Campaign"
|
||||
msgstr "活動"
|
||||
|
||||
msgid "Subscriber ID"
|
||||
msgstr "訂閱者 ID"
|
||||
|
||||
msgid "Retry Count"
|
||||
msgstr "重試次數"
|
||||
|
||||
msgid "Next Retry At"
|
||||
msgstr "下次重試時間"
|
||||
|
||||
msgid "Response Status"
|
||||
msgstr "回應狀態碼"
|
||||
|
||||
msgid "Response Payload"
|
||||
msgstr "回應內容"
|
||||
|
||||
msgid "Error Message"
|
||||
msgstr "錯誤訊息"
|
||||
|
||||
msgid "Subscribe Subject Template"
|
||||
msgstr "訂閱主旨模板"
|
||||
|
||||
msgid "Subscribe HTML Template"
|
||||
msgstr "訂閱 HTML 模板"
|
||||
|
||||
msgid "Subscribe Text Template"
|
||||
msgstr "訂閱純文字模板"
|
||||
|
||||
msgid "Confirm Success Template"
|
||||
msgstr "確認成功模板"
|
||||
|
||||
msgid "Confirm Failure Template"
|
||||
msgstr "確認失敗模板"
|
||||
|
||||
msgid "Unsubscribe Intro Template"
|
||||
msgstr "退訂前置說明模板"
|
||||
|
||||
msgid "Unsubscribe Success Template"
|
||||
msgstr "退訂成功模板"
|
||||
|
||||
msgid "Unsubscribe Failure Template"
|
||||
msgstr "退訂失敗模板"
|
||||
|
||||
msgid "Leave blank to use member_center_list_id from Newsletter System Settings."
|
||||
msgstr "留空時會使用 Newsletter System Settings 的 member_center_list_id。"
|
||||
|
||||
msgid "Use image picker/upload from Wagtail. Prefer image URLs instead of Base64 in CMS."
|
||||
msgstr "可用圖片按鈕從 Wagtail 圖庫選圖或上傳。建議使用圖片 URL,不要在 CMS 端轉 Base64。"
|
||||
|
||||
msgid "Send now"
|
||||
msgstr "馬上發送"
|
||||
|
||||
msgid "Send test email (save settings before sending)."
|
||||
msgstr "發送測試郵件(請先儲存設定後再發送測試郵件)"
|
||||
|
||||
msgid "Send email to"
|
||||
msgstr "發送郵件到"
|
||||
|
||||
msgid "This field is only for this test and will not be saved."
|
||||
msgstr "此欄位僅作本次測試,不會儲存。"
|
||||
|
||||
msgid "Send test email"
|
||||
msgstr "發送測試郵件"
|
||||
@ -147,6 +147,10 @@ USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
LOCALE_PATHS = [
|
||||
os.path.join(BASE_DIR, "locale"),
|
||||
]
|
||||
|
||||
# --- Wagtail embeds (Instagram/FB via Graph oEmbed) ---
|
||||
# Reads a token from env and adds an extra oEmbed finder for IG/FB.
|
||||
# Keeps the default oEmbed finder first for YouTube/Vimeo/etc.
|
||||
|
||||
BIN
innovedus_cms/mysite/static/favicon.ico
Normal file
BIN
innovedus_cms/mysite/static/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 728 B |
@ -17,6 +17,8 @@
|
||||
<meta name="description" content="{{ page.search_description }}" />
|
||||
{% endif %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" href="{% static 'favicon.ico' %}" type="image/x-icon">
|
||||
<link rel="shortcut icon" href="{% static 'favicon.ico' %}" type="image/x-icon">
|
||||
{% if ga4_measurement_id %}
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id={{ ga4_measurement_id }}"></script>
|
||||
<script>
|
||||
|
||||
@ -28,12 +28,12 @@
|
||||
{% if form.smtp_relay_host and form.smtp_password %}
|
||||
<section class="w-panel w-mt-8">
|
||||
<div class="w-panel__header">
|
||||
<h2 class="w-panel__heading">發送測試郵件(請先儲存設定後再發送測試郵件)</h2>
|
||||
<h2 class="w-panel__heading">{% trans "Send test email (save settings before sending)." %}</h2>
|
||||
</div>
|
||||
<div class="w-panel__content">
|
||||
<ul class="fields">
|
||||
<li>
|
||||
<label class="w-block w-font-semibold w-mb-2" for="smtp_test_email">發送郵件到</label>
|
||||
<label class="w-block w-font-semibold w-mb-2" for="smtp_test_email">{% trans "Send email to" %}</label>
|
||||
<input
|
||||
class="w-input"
|
||||
type="email"
|
||||
@ -42,7 +42,7 @@
|
||||
value="{{ form.data.smtp_test_email|default:'' }}"
|
||||
placeholder="name@example.com"
|
||||
>
|
||||
<p class="help">此欄位僅作本次測試,不會儲存。</p>
|
||||
<p class="help">{% trans "This field is only for this test and will not be saved." %}</p>
|
||||
</li>
|
||||
</ul>
|
||||
<button
|
||||
@ -52,7 +52,7 @@
|
||||
formmethod="post"
|
||||
formnovalidate
|
||||
>
|
||||
發送測試郵件
|
||||
{% trans "Send test email" %}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user