Compare commits

...
Author SHA1 Message Date
HampusandGitHub 34b6ecfbd2 chore(admin): remove the heap snapshot endpoint (#2814) 2026-09-16 18:56:01 +02:00
HampusandGitHub 4ef9c4c65b fix(api): restore commas in geoip location labels (#2812) 2026-09-16 18:27:50 +02:00
HampusandGitHub 3276039e41 feat(admin): audit admin reads and filter the log by access (#2811) 2026-09-16 17:23:03 +02:00
HampusandGitHub 03d1354562 chore(voice): remove voice reconciliation leftovers (#2810) 2026-09-16 17:09:57 +02:00
HampusandGitHub 964845d7a7 chore(voice): remove the recon service (#2808) 2026-09-16 16:53:30 +02:00
HampusandGitHub 3bc5dd8e0f fix(gateway): always clear expired custom statuses (#2807) 2026-09-16 16:52:22 +02:00
HampusandGitHub 17292fd6a5 fix(app): stop plain unicode symbols rendering as color emoji (#2806) 2026-09-16 16:33:13 +02:00
TarekandGitHub f753659899 feat(instance): make the status page URL configurable (#1159) 2026-09-16 15:20:37 +02:00
HampusandGitHub 7412ec3395 refactor(api): purge cache by canonical media prefix (#2802) 2026-09-16 02:32:36 +02:00
HampusandGitHub 570c8776c4 fix(api): require manage messages to remove others' reactions (#2799) 2026-09-15 18:16:55 +02:00
HampusandGitHub 910db6734b feat(experiments): ship seven treatments to everyone (#2798) 2026-09-15 18:03:35 +02:00
HampusandGitHub 9e614026d7 fix(api): stop exporting the change feed stats type (#2797) 2026-09-15 17:27:09 +02:00
HampusandGitHub b38e7c6433 feat(api): publish object storage changes to a JetStream feed (#2796) 2026-09-15 17:20:26 +02:00
HampusandGitHub 83c8e91955 fix(app): fit the user area popout shadow to its card (#2795) 2026-09-15 17:11:43 +02:00
HampusandGitHub 0532dd0440 fix(app): stop guild banner jumps and restore hover animation (#2792) 2026-09-15 09:31:52 +02:00
HampusandGitHub a08615e306 fix(gateway): match member search on username and global name (#2791) 2026-09-15 08:48:33 +02:00
HampusandGitHub f4c5fee17e feat(app): rank forward destinations and preview the message (#2790) 2026-09-15 07:23:26 +02:00
HampusandGitHub c5aaf65a10 fix(app): list friends with closed DMs in the forward modal (#2787) 2026-09-15 00:39:18 +02:00
HampusandGitHub 951e39da3d feat(api): add expression source guild routes (#2786) 2026-09-15 00:31:47 +02:00
HampusandGitHub b693d84d2b fix(openapi): restore named discriminated union branches (#2785) 2026-09-14 23:42:26 +02:00
HampusandGitHub 9bbf6c513b fix(installer): say what the email prompt is for (#2784) 2026-09-14 23:07:03 +02:00
HampusandGitHub 50cec92738 fix(api): batch member user lookups on guild load (#2783) 2026-09-14 23:06:36 +02:00
HampusandGitHub c212d315f4 fix(app): gate reworked typing indicators behind an experiment (#2782) 2026-09-14 21:54:52 +02:00
HampusandGitHub 1861432a53 fix(app): scope message rings and reach the composer by key (#2781) 2026-09-14 21:44:12 +02:00
HampusandGitHub d0c6146429 feat(app): gate a collapsing guild header behind an experiment (#2779) 2026-09-14 21:15:31 +02:00
HampusandGitHub 550e6b05a1 fix(app): show hover highlight and inset the focus ring (#2778) 2026-09-14 20:51:59 +02:00
HampusandGitHub 5b6949170f chore(donations): drop the donor email case backfill script (#2776) 2026-09-14 20:45:38 +02:00
HampusandGitHub f32bc37794 fix(guild): correct activity log sentence presentation (#2777) 2026-09-14 20:43:08 +02:00
HampusandGitHub 8ee2279b4b feat(donations): add Nordic currencies, raise amount ceilings (#2775) 2026-09-14 20:27:24 +02:00
HampusandGitHub 6339c3b8ad feat(app): gate the expression info card behind an experiment (#2773) 2026-09-14 20:14:58 +02:00
HampusandGitHub 7c9274847f feat(gateway): list bot ready guilds as unavailable (#2774) 2026-09-14 20:08:26 +02:00
HampusandGitHub 5d1dddc093 fix(deps): update rustls for RUSTSEC-2026-0285 (#2772) 2026-09-14 19:01:22 +02:00
HampusandGitHub 04481d7235 fix(app): keep blockquotes open across pasted lines (#2771) 2026-09-14 18:54:04 +02:00
Hampus Kraft a3d6cf37cb fix(guild): render activity log entries deterministically (#2766) 2026-09-14 17:39:19 +02:00
HampusandGitHub ed10f9d323 fix(app): gate blocked group rendering behind an experiment (#2763) 2026-09-14 16:08:19 +02:00
HampusandGitHub 4b278a0da8 fix(app): gate one-Tab message focus behind an experiment (#2762) 2026-09-14 15:55:00 +02:00
HampusandGitHub 1281045648 fix(app): gate single-source message hover behind an experiment (#2761) 2026-09-14 15:37:37 +02:00
HampusandGitHub 69c42cff90 docs: reword vague sentences and fix wrong claims (#2760) 2026-09-14 15:18:53 +02:00
HampusandGitHub 7d56481aba fix(app): copy selected message text without markdown (#2759) 2026-09-14 14:53:53 +02:00
699 changed files with 108101 additions and 73012 deletions
-3
View File
@@ -28,9 +28,6 @@ f:media_proxy:
f:messages:
- changed-files:
- any-glob-to-any-file: fluxer_messages/**/*
f:recon:
- changed-files:
- any-glob-to-any-file: fluxer_recon/**/*
f:snowflakes:
- changed-files:
- any-glob-to-any-file: fluxer_snowflakes/**/*
-36
View File
@@ -1,36 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
name: build recon
on:
workflow_dispatch:
inputs:
build-version:
description: "Explicit Fluxer CalVer build version (YYYY.MDD.MICRO, UTC HHMMSS without leading zeroes) to use instead of automatic UTC clock allocation"
type: string
required: false
default: ""
permissions:
actions: read
contents: write
packages: write
jobs:
approve:
name: approve build release
permissions: {}
runs-on: ubuntu-24.04
environment: builds
timeout-minutes: 5
steps:
- name: approved
run: echo "Build release approved."
image:
needs: approve
uses: ./.github/workflows/_build-image.yaml
secrets: inherit
with:
image: fluxer-recon
dockerfile: fluxer_recon/Dockerfile
build-version: ${{ inputs['build-version'] }}
Generated
+14 -32
View File
@@ -266,9 +266,9 @@ dependencies = [
[[package]]
name = "aws-lc-rs"
version = "1.16.3"
version = "1.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f"
checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e"
dependencies = [
"aws-lc-sys",
"zeroize",
@@ -276,14 +276,15 @@ dependencies = [
[[package]]
name = "aws-lc-sys"
version = "0.40.0"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7"
checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27"
dependencies = [
"cc",
"cmake",
"dunce",
"fs_extra",
"pkg-config",
]
[[package]]
@@ -1622,7 +1623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -1867,25 +1868,6 @@ dependencies = [
"url",
]
[[package]]
name = "fluxer-recon"
version = "0.0.0"
dependencies = [
"anyhow",
"axum",
"base64",
"fluxer-svc",
"hmac 0.13.0",
"reqwest",
"scylla",
"serde",
"serde_json",
"sha2 0.11.0",
"thiserror",
"tokio",
"tracing",
]
[[package]]
name = "fluxer-snowflakes"
version = "0.1.0"
@@ -3988,14 +3970,14 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
name = "rustls"
version = "0.23.40"
version = "0.23.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b"
checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634"
dependencies = [
"aws-lc-rs",
"log",
@@ -4047,7 +4029,7 @@ dependencies = [
"security-framework",
"security-framework-sys",
"webpki-root-certs",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -4058,9 +4040,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]]
name = "rustls-webpki"
version = "0.103.13"
version = "0.103.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2"
dependencies = [
"aws-lc-rs",
"ring",
@@ -4671,7 +4653,7 @@ dependencies = [
"getrandom 0.4.2",
"once_cell",
"rustix",
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@@ -5539,7 +5521,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
-1
View File
@@ -15,7 +15,6 @@ members = [
"fluxer_users",
"fluxer_unfurl",
"packages/markdown_parser/rust",
"fluxer_recon",
]
exclude = [
"packages/markdown_parser/rust/fuzz",
-5
View File
@@ -65,11 +65,6 @@ FLUXER_LIVEKIT_API_SECRET=fluxer-livekit-development-secret
FLUXER_LIVEKIT_WEBHOOK_URL=http://localhost:8088/api/webhooks/livekit
FLUXER_LIVEKIT_DEFAULT_REGION={"id":"local","name":"Local","emoji":"LC","latitude":59.3293,"longitude":18.0686}
FLUXER_RECON_MODE=observing
FLUXER_RECON_EXPECTED_ROOMS=64
FLUXER_RECON_WARMUP_SECONDS=15
FLUXER_RECON_MAX_HOT_ROOMS=8
FLUXER_API_PORT=8080
FLUXER_API_PRESIGNED_ATTACHMENT_UPLOADS_ENABLED=true
FLUXER_API_WORKER_MODE=all_lanes
+3 -2
View File
@@ -365,8 +365,9 @@ FLUXER_DISCOVERY_ENABLED=true
# messages, 320 for snowflakes and 64 elsewhere, and they govern every service
# Compose does not forward this to. A router holds a slot for the whole round
# trip to its shard, so this is a ceiling on requests in flight at once and not a
# rate: too low a value does not slow requests down, it rejects them, and the api
# turns that rejection into a 503.
# rate: too low a value does not slow requests down, it rejects them. The api
# turns that rejection into a 503 and logs "shard rejected the request because
# it is at its concurrency limit".
#FLUXER_SVC_MAX_CONCURRENT_REQUESTS=192
# The api and the Rust services name their fixed Postgres statement shapes so the
-1
View File
@@ -413,7 +413,6 @@ services:
NODE_OPTIONS: --enable-source-maps${FLUXER_WORKER_NODE_HEAP_MB:+ --max-old-space-size=$FLUXER_WORKER_NODE_HEAP_MB}
FLUXER_API_WORKER_MODE: all_lanes
FLUXER_API_WORKER_ENABLE_CRON_SCHEDULER: "true"
FLUXER_API_WORKER_ENABLE_VOICE_RECONCILIATION: "true"
FLUXER_POSTGRES_MAX_CONNECTIONS: "25"
healthcheck:
test: ["CMD", "node", "-e", "const age=Date.now()-require('node:fs').statSync('/tmp/fluxer-worker-heartbeat').mtimeMs;if(age>30000){console.error('worker heartbeat is '+Math.round(age)+'ms old');process.exit(1)}"]
+301
View File
@@ -38,6 +38,8 @@ fn generate_admin_api(manifest_dir: &Path, out_dir: &Path) {
let mut spec: openapiv3::OpenAPI =
serde_json::from_str(&json_str).expect("failed to parse openapi-admin.json");
adapt_progenitor_throttled_errors(&mut spec);
relax_guild_audit_log_schemas(&mut spec);
relax_progenitor_schema_strictness(&mut spec);
let mut settings = progenitor::GenerationSettings::new();
settings.with_interface(progenitor::InterfaceStyle::Positional);
@@ -141,6 +143,305 @@ fn adapt_progenitor_throttled_errors(spec: &mut openapiv3::OpenAPI) {
}
}
fn relax_guild_audit_log_schemas(spec: &mut openapiv3::OpenAPI) {
let components = spec.components.as_mut().expect("missing API components");
let entry = object_schema_mut(components, "GuildAuditLogEntryResponse");
entry.additional_properties = None;
let openapiv3::ReferenceOr::Item(options) = entry
.properties
.get_mut("options")
.expect("GuildAuditLogEntryResponse has no options property")
else {
panic!("GuildAuditLogEntryResponse options must be an inline schema");
};
let openapiv3::SchemaKind::Type(openapiv3::Type::Object(options)) = &mut options.schema_kind
else {
panic!("GuildAuditLogEntryResponse options must be an object schema");
};
options.additional_properties = None;
let change = object_schema_mut(components, "AuditLogChangeSchema");
change.additional_properties = None;
for property in ["old_value", "new_value"] {
change.properties.insert(
property.to_string(),
openapiv3::ReferenceOr::Item(Box::new(openapiv3::Schema {
schema_data: openapiv3::SchemaData::default(),
schema_kind: openapiv3::SchemaKind::Any(openapiv3::AnySchema::default()),
})),
);
}
}
fn object_schema_mut<'a>(
components: &'a mut openapiv3::Components,
name: &str,
) -> &'a mut openapiv3::ObjectType {
let Some(openapiv3::ReferenceOr::Item(schema)) = components.schemas.get_mut(name) else {
panic!("missing inline {name} schema");
};
let openapiv3::SchemaKind::Type(openapiv3::Type::Object(object)) = &mut schema.schema_kind
else {
panic!("{name} must be an object schema");
};
object
}
const MAX_SCHEMA_REFERENCE_DEPTH: usize = 32;
fn relax_progenitor_schema_strictness(spec: &mut openapiv3::OpenAPI) {
let registry = spec.components.clone().unwrap_or_default();
if let Some(components) = spec.components.as_mut() {
for schema in components.schemas.values_mut() {
relax_schema_reference(schema, &registry);
}
for response in components.responses.values_mut() {
if let openapiv3::ReferenceOr::Item(response) = response {
relax_response(response, &registry);
}
}
for parameter in components.parameters.values_mut() {
if let openapiv3::ReferenceOr::Item(parameter) = parameter {
relax_parameter(parameter, &registry);
}
}
for request_body in components.request_bodies.values_mut() {
if let openapiv3::ReferenceOr::Item(request_body) = request_body {
relax_content(&mut request_body.content, &registry);
}
}
for header in components.headers.values_mut() {
if let openapiv3::ReferenceOr::Item(header) = header {
relax_parameter_format(&mut header.format, &registry);
}
}
}
for path in spec.paths.paths.values_mut() {
let openapiv3::ReferenceOr::Item(path) = path else {
continue;
};
for parameter in &mut path.parameters {
if let openapiv3::ReferenceOr::Item(parameter) = parameter {
relax_parameter(parameter, &registry);
}
}
for operation in [
&mut path.get,
&mut path.put,
&mut path.post,
&mut path.delete,
&mut path.options,
&mut path.head,
&mut path.patch,
&mut path.trace,
]
.into_iter()
.flatten()
{
for parameter in &mut operation.parameters {
if let openapiv3::ReferenceOr::Item(parameter) = parameter {
relax_parameter(parameter, &registry);
}
}
if let Some(openapiv3::ReferenceOr::Item(request_body)) =
operation.request_body.as_mut()
{
relax_content(&mut request_body.content, &registry);
}
for response in operation
.responses
.responses
.values_mut()
.chain(operation.responses.default.iter_mut())
{
if let openapiv3::ReferenceOr::Item(response) = response {
relax_response(response, &registry);
}
}
}
}
}
fn relax_response(response: &mut openapiv3::Response, registry: &openapiv3::Components) {
relax_content(&mut response.content, registry);
for header in response.headers.values_mut() {
if let openapiv3::ReferenceOr::Item(header) = header {
relax_parameter_format(&mut header.format, registry);
}
}
}
fn relax_content(content: &mut openapiv3::Content, registry: &openapiv3::Components) {
for media_type in content.values_mut() {
if let Some(schema) = media_type.schema.as_mut() {
relax_schema_reference(schema, registry);
}
}
}
fn relax_parameter(parameter: &mut openapiv3::Parameter, registry: &openapiv3::Components) {
let format = match parameter {
openapiv3::Parameter::Query { parameter_data, .. }
| openapiv3::Parameter::Header { parameter_data, .. }
| openapiv3::Parameter::Path { parameter_data, .. }
| openapiv3::Parameter::Cookie { parameter_data, .. } => &mut parameter_data.format,
};
relax_parameter_format(format, registry);
}
fn relax_parameter_format(
format: &mut openapiv3::ParameterSchemaOrContent,
registry: &openapiv3::Components,
) {
match format {
openapiv3::ParameterSchemaOrContent::Schema(schema) => {
relax_schema_reference(schema, registry)
}
openapiv3::ParameterSchemaOrContent::Content(content) => relax_content(content, registry),
}
}
fn relax_schema_reference(
schema: &mut openapiv3::ReferenceOr<openapiv3::Schema>,
registry: &openapiv3::Components,
) {
if let openapiv3::ReferenceOr::Item(schema) = schema {
relax_schema(schema, registry);
}
}
fn relax_boxed_schema_reference(
schema: &mut openapiv3::ReferenceOr<Box<openapiv3::Schema>>,
registry: &openapiv3::Components,
) {
if let openapiv3::ReferenceOr::Item(schema) = schema {
relax_schema(schema, registry);
}
}
fn relax_schema(schema: &mut openapiv3::Schema, registry: &openapiv3::Components) {
if flattens_objects_beside_scalars(&schema.schema_kind, registry) {
schema.schema_kind = openapiv3::SchemaKind::Any(openapiv3::AnySchema::default());
return;
}
match &mut schema.schema_kind {
openapiv3::SchemaKind::Type(openapiv3::Type::Object(object)) => {
relax_additional_properties(&mut object.additional_properties, registry);
for property in object.properties.values_mut() {
relax_boxed_schema_reference(property, registry);
}
}
openapiv3::SchemaKind::Type(openapiv3::Type::Array(array)) => {
if let Some(items) = array.items.as_mut() {
relax_boxed_schema_reference(items, registry);
}
}
openapiv3::SchemaKind::Type(_) => {}
openapiv3::SchemaKind::OneOf { one_of: subschemas }
| openapiv3::SchemaKind::AllOf { all_of: subschemas }
| openapiv3::SchemaKind::AnyOf { any_of: subschemas } => {
for subschema in subschemas {
relax_schema_reference(subschema, registry);
}
}
openapiv3::SchemaKind::Not { not } => relax_schema_reference(not, registry),
openapiv3::SchemaKind::Any(any) => {
relax_additional_properties(&mut any.additional_properties, registry);
for property in any.properties.values_mut() {
relax_boxed_schema_reference(property, registry);
}
if let Some(items) = any.items.as_mut() {
relax_boxed_schema_reference(items, registry);
}
for subschema in any
.one_of
.iter_mut()
.chain(any.all_of.iter_mut())
.chain(any.any_of.iter_mut())
{
relax_schema_reference(subschema, registry);
}
if let Some(not) = any.not.as_mut() {
relax_schema_reference(not, registry);
}
}
}
}
fn relax_additional_properties(
additional_properties: &mut Option<openapiv3::AdditionalProperties>,
registry: &openapiv3::Components,
) {
match additional_properties {
Some(openapiv3::AdditionalProperties::Any(false)) => *additional_properties = None,
Some(openapiv3::AdditionalProperties::Schema(schema)) => {
relax_schema_reference(schema, registry)
}
_ => {}
}
}
fn flattens_objects_beside_scalars(
schema_kind: &openapiv3::SchemaKind,
registry: &openapiv3::Components,
) -> bool {
let subschemas = match schema_kind {
openapiv3::SchemaKind::OneOf { one_of } => one_of,
openapiv3::SchemaKind::AnyOf { any_of } => any_of,
_ => return false,
};
let mut objects = false;
let mut scalars = false;
for subschema in subschemas {
if resolves_to_object(subschema, registry, MAX_SCHEMA_REFERENCE_DEPTH) {
objects = true;
} else {
scalars = true;
}
}
objects && scalars
}
fn resolves_to_object(
schema: &openapiv3::ReferenceOr<openapiv3::Schema>,
registry: &openapiv3::Components,
depth: usize,
) -> bool {
let Some(depth) = depth.checked_sub(1) else {
return false;
};
let schema = match schema {
openapiv3::ReferenceOr::Reference { reference } => {
let Some(target) = reference
.strip_prefix("#/components/schemas/")
.and_then(|name| registry.schemas.get(name))
else {
return false;
};
return resolves_to_object(target, registry, depth);
}
openapiv3::ReferenceOr::Item(schema) => schema,
};
match &schema.schema_kind {
openapiv3::SchemaKind::Type(openapiv3::Type::Object(_)) => true,
openapiv3::SchemaKind::Type(_) => false,
openapiv3::SchemaKind::OneOf { one_of: subschemas }
| openapiv3::SchemaKind::AllOf { all_of: subschemas }
| openapiv3::SchemaKind::AnyOf { any_of: subschemas } => subschemas
.iter()
.any(|subschema| resolves_to_object(subschema, registry, depth)),
openapiv3::SchemaKind::Not { .. } => false,
openapiv3::SchemaKind::Any(any) => {
any.typ.as_deref() == Some("object")
|| !any.properties.is_empty()
|| any.additional_properties.is_some()
}
}
}
struct Face {
css_family: String,
weight: u64,
+57 -69
View File
@@ -933,6 +933,22 @@
},
"description": "Filter by target entity ID (user, channel, role, invite code, etc.)"
},
{
"name": "access",
"in": "query",
"required": false,
"schema": {
"description": "Only return entries recorded by reads or only entries recorded by writes",
"x-enumNames": ["read", "write"],
"x-enumDescriptions": [
"An entry recorded by an operation that only reads data",
"An entry recorded by an operation that changes data or triggers work"
],
"enum": ["read", "write"],
"type": "string"
},
"description": "Only return entries recorded by reads or only entries recorded by writes"
},
{
"name": "sort_by",
"in": "query",
@@ -5427,59 +5443,6 @@
}
}
},
"/admin/system/heap-snapshots": {
"post": {
"operationId": "create_admin_system_heap_snapshot",
"summary": "Create a V8 heap snapshot",
"tags": ["Admin"],
"responses": {
"200": {
"description": "Success",
"content": {"application/octet-stream": {"schema": {"$ref": "#/components/schemas/HeapSnapshotResponse"}}}
},
"400": {
"description": "Bad Request - The request was malformed or contained invalid data",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
},
"401": {
"description": "Unauthorized - Authentication is required or the token is invalid",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
},
"403": {
"description": "Forbidden - You do not have permission to perform this action",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
},
"429": {
"description": "Too Many Requests - You are being rate limited",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ThrottledError"}}},
"headers": {
"Retry-After": {
"description": "Number of seconds to wait before retrying (only on 429)",
"schema": {"type": "integer"}
},
"X-RateLimit-Limit": {
"description": "The number of requests that can be made in the current window",
"schema": {"type": "integer"}
},
"X-RateLimit-Remaining": {
"description": "The number of remaining requests that can be made",
"schema": {"type": "integer"}
},
"X-RateLimit-Reset": {
"description": "Unix timestamp when the rate limit resets",
"schema": {"type": "integer"}
}
}
},
"500": {
"description": "Internal Server Error - An unexpected error occurred",
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
}
},
"description": "Writes a V8 heap snapshot of the current process and returns the snapshot file. Used for diagnosing memory leaks. Requires SYSTEM_HEAP_SNAPSHOT permission.",
"security": [{"adminApiKey": []}]
}
},
"/admin/users": {
"get": {
"operationId": "list_admin_users",
@@ -9874,7 +9837,7 @@
"type": "object",
"properties": {
"acls": {
"maxItems": 112,
"maxItems": 111,
"type": "array",
"items": {"$ref": "#/components/schemas/AdminAclType"},
"description": "List of access control permissions to assign"
@@ -9904,7 +9867,6 @@
"required": ["users", "total"],
"additionalProperties": false
},
"HeapSnapshotResponse": {"type": "string", "format": "binary", "description": "V8 heap snapshot file"},
"SendSystemDmRequest": {
"type": "object",
"properties": {
@@ -10671,7 +10633,9 @@
"logo_url": {"nullable": true, "type": "string"},
"wordmark_url": {"nullable": true, "type": "string"},
"favicon_url": {"nullable": true, "type": "string"},
"theme_color": {"nullable": true, "type": "string"}
"theme_color": {"nullable": true, "type": "string"},
"status_page_url": {"nullable": true, "type": "string"},
"status_page_incident_history_url": {"nullable": true, "type": "string"}
},
"required": [
"product_name",
@@ -10680,7 +10644,9 @@
"logo_url",
"wordmark_url",
"favicon_url",
"theme_color"
"theme_color",
"status_page_url",
"status_page_incident_history_url"
],
"additionalProperties": false
},
@@ -11144,7 +11110,9 @@
"logo_url": {"nullable": true, "type": "string", "maxLength": 2048},
"wordmark_url": {"nullable": true, "type": "string", "maxLength": 2048},
"favicon_url": {"nullable": true, "type": "string", "maxLength": 2048},
"theme_color": {"nullable": true, "type": "string", "maxLength": 64}
"theme_color": {"nullable": true, "type": "string", "maxLength": 64},
"status_page_url": {"nullable": true, "type": "string", "maxLength": 2048},
"status_page_incident_history_url": {"nullable": true, "type": "string", "maxLength": 2048}
}
},
"setup": {"nullable": true, "type": "object", "properties": {"configured": {"type": "boolean"}}},
@@ -12621,6 +12589,16 @@
}
},
"action": {"type": "string", "minLength": 1, "maxLength": 256},
"access": {
"description": "Whether the recorded operation read data or changed it",
"x-enumNames": ["read", "write"],
"x-enumDescriptions": [
"An entry recorded by an operation that only reads data",
"An entry recorded by an operation that changes data or triggers work"
],
"enum": ["read", "write"],
"type": "string"
},
"audit_log_reason": {"nullable": true, "type": "string", "minLength": 1, "maxLength": 4000},
"metadata": {"type": "object", "additionalProperties": {"type": "string", "maxLength": 4000}},
"created_at": {"type": "string"}
@@ -12638,6 +12616,7 @@
"related_guilds",
"related_channels",
"action",
"access",
"audit_log_reason",
"metadata",
"created_at"
@@ -12726,7 +12705,7 @@
},
"acls": {
"description": "Replacement list of access control permissions for the key",
"maxItems": 112,
"maxItems": 111,
"type": "array",
"items": {"$ref": "#/components/schemas/AdminAclType"}
}
@@ -12744,7 +12723,7 @@
"type": "string"
},
"acls": {
"maxItems": 112,
"maxItems": 111,
"type": "array",
"items": {"type": "string"},
"description": "List of access control permissions for the key"
@@ -12774,7 +12753,7 @@
"maximum": 365
},
"acls": {
"maxItems": 112,
"maxItems": 111,
"type": "array",
"items": {"$ref": "#/components/schemas/AdminAclType"},
"description": "List of access control permissions for the key"
@@ -12795,7 +12774,7 @@
"type": "string"
},
"acls": {
"maxItems": 112,
"maxItems": 111,
"type": "array",
"items": {"type": "string"},
"description": "List of access control permissions for the key"
@@ -12808,7 +12787,7 @@
"type": "object",
"properties": {
"acls": {
"maxItems": 112,
"maxItems": 111,
"type": "array",
"items": {"type": "string", "minLength": 1, "maxLength": 64},
"description": "Every admin access control permission the admin API recognises"
@@ -12899,7 +12878,6 @@
"report:view",
"report:view:reporter_pii",
"system_dm:send",
"system:heap_snapshot",
"user:cancel:bulk_message_deletion",
"user:delete",
"user:disable:suspicious",
@@ -14917,7 +14895,10 @@
"nullable": true,
"type": "string"
},
"reason": {"description": "The reason provided for the action", "type": "string"},
"reason": {
"description": "The audit log reason. For bans and timeouts without an X-Audit-Log-Reason header this is the reason sent in the request body",
"type": "string"
},
"options": {
"description": "Additional options depending on action type",
"type": "object",
@@ -14925,15 +14906,22 @@
"channel_id": {"description": "Channel ID for relevant actions", "type": "string"},
"count": {"description": "Count of items affected", "type": "number"},
"delete_member_days": {
"description": "Number of days of messages to delete on member ban",
"description": "Deprecated. Whole days of messages deleted, written only by bans recorded before delete_message_seconds",
"type": "string"
},
"delete_message_seconds": {
"description": "Seconds of the banned user's messages that the ban deleted, present only when positive",
"type": "number"
},
"id": {"description": "ID of the affected entity", "type": "string"},
"integration_type": {"description": "Type of integration", "type": "number"},
"message_id": {"description": "Message ID for relevant actions", "type": "string"},
"members_removed": {"description": "Number of members removed", "type": "number"},
"role_name": {"description": "Name of the role", "type": "string"},
"type": {"description": "Type identifier", "type": "number"},
"role_name": {"description": "Name of the role when the entry was written", "type": "string"},
"type": {
"description": "Channel type for CHANNEL_CREATE, CHANNEL_UPDATE and CHANNEL_DELETE. Overwrite target type (0 role, 1 member) for CHANNEL_OVERWRITE_*",
"type": "number"
},
"inviter_id": {"description": "ID of the user who created the invite", "type": "string"},
"max_age": {"description": "Maximum age of the invite in seconds", "type": "number"},
"max_uses": {"description": "Maximum number of uses for the invite", "type": "number"},
@@ -15428,7 +15416,7 @@
"pending_bulk_message_deletion_at": {"nullable": true, "type": "string"},
"deletion_reason_code": {"nullable": true, "allOf": [{"$ref": "#/components/schemas/Int32Type"}]},
"deletion_public_reason": {"nullable": true, "type": "string"},
"acls": {"maxItems": 112, "type": "array", "items": {"type": "string"}},
"acls": {"maxItems": 111, "type": "array", "items": {"type": "string"}},
"traits": {"maxItems": 100, "type": "array", "items": {"type": "string"}},
"has_totp": {"type": "boolean"},
"authenticator_types": {"maxItems": 10, "type": "array", "items": {"$ref": "#/components/schemas/Int32Type"}},
-2
View File
@@ -79,7 +79,6 @@ pub const REPORT_RESOLVE: &str = "report:resolve";
pub const REPORT_VIEW: &str = "report:view";
pub const REPORT_VIEW_REPORTER_PII: &str = "report:view:reporter_pii";
pub const SYSTEM_DM_SEND: &str = "system_dm:send";
pub const SYSTEM_HEAP_SNAPSHOT: &str = "system:heap_snapshot";
pub const USER_CANCEL_BULK_MESSAGE_DELETION: &str = "user:cancel:bulk_message_deletion";
pub const USER_DELETE: &str = "user:delete";
pub const USER_DISABLE_SUSPICIOUS: &str = "user:disable:suspicious";
@@ -192,7 +191,6 @@ pub const ALL_ACLS: &[&str] = &[
REPORT_VIEW,
REPORT_VIEW_REPORTER_PII,
SYSTEM_DM_SEND,
SYSTEM_HEAP_SNAPSHOT,
USER_CANCEL_BULK_MESSAGE_DELETION,
USER_DELETE,
USER_DISABLE_SUSPICIOUS,
+41
View File
@@ -10,6 +10,7 @@ pub struct SearchAuditLogsParams {
pub admin_user_id: Option<String>,
pub target_id: Option<String>,
pub target_type: Option<String>,
pub access: Option<String>,
pub sort_by: Option<String>,
pub sort_order: Option<String>,
pub limit: u32,
@@ -21,6 +22,12 @@ impl AdminApiClient {
&self,
params: &SearchAuditLogsParams,
) -> ApiResult<AuditLogsListResponse> {
let access = params
.access
.as_deref()
.map(audit_access)
.transpose()?
.map(|value| value.to_string());
let sort_by = params
.sort_by
.as_deref()
@@ -46,6 +53,7 @@ impl AdminApiClient {
params.target_type.as_deref().unwrap_or_default(),
),
("target_id", params.target_id.as_deref().unwrap_or_default()),
("access", access.as_deref().unwrap_or_default()),
("sort_by", sort_by.as_deref().unwrap_or_default()),
("sort_order", sort_order.as_deref().unwrap_or_default()),
("limit", limit.as_str()),
@@ -55,6 +63,11 @@ impl AdminApiClient {
}
}
fn audit_access(value: &str) -> ApiResult<generated_types::ListAdminAuditLogsAccess> {
generated_types::ListAdminAuditLogsAccess::try_from(value)
.map_err(|e| ApiError::Parse(e.to_string()))
}
fn audit_sort_by(value: &str) -> ApiResult<generated_types::ListAdminAuditLogsSortBy> {
let value = match value {
"created_at" => "createdAt",
@@ -83,6 +96,13 @@ mod tests {
assert_eq!(audit_sort_order("desc").unwrap().to_string(), "desc");
}
#[test]
fn accepts_only_known_access_filters() {
assert_eq!(audit_access("read").unwrap().to_string(), "read");
assert_eq!(audit_access("write").unwrap().to_string(), "write");
assert!(audit_access("all").is_err());
}
#[test]
fn rejects_lossy_audit_totals() {
for total in [serde_json::json!(1.5), serde_json::json!(-1)] {
@@ -99,6 +119,7 @@ mod tests {
"admin_user_id": "234567890123456789",
"admin_user": null,
"action": "USER_UPDATE",
"access": "write",
"target_id": "345678901234567890",
"target_type": "user",
"target_user": null,
@@ -118,6 +139,26 @@ mod tests {
assert_eq!(generated.logs[0].action.to_string(), "USER_UPDATE");
let response: AuditLogsListResponse = serde_json::from_value(json.clone()).unwrap();
assert_eq!(response.logs[0].access.as_deref(), Some("write"));
assert_eq!(serde_json::to_value(response).unwrap(), json);
}
#[test]
fn deserializes_audit_entries_from_an_api_without_access() {
let json = serde_json::json!({
"logs": [{
"log_id": "123456789012345678",
"admin_user_id": "234567890123456789",
"action": "USER_UPDATE",
"target_id": "345678901234567890",
"target_type": "user",
"audit_log_reason": null,
"metadata": {},
"created_at": "2026-09-11T12:00:00.000Z"
}],
"total": 1
});
let response: AuditLogsListResponse = serde_json::from_value(json).unwrap();
assert_eq!(response.logs[0].access, None);
}
}
+2
View File
@@ -9,6 +9,8 @@ pub struct AuditLogEntry {
#[serde(default)]
pub admin_user: Option<AuditLogUserSummary>,
pub action: String,
#[serde(default)]
pub access: Option<String>,
pub target_id: String,
pub target_type: String,
#[serde(default)]
@@ -326,6 +326,8 @@ pub struct AppBrandingConfigResponse {
pub wordmark_url: Option<String>,
pub favicon_url: Option<String>,
pub theme_color: Option<String>,
pub status_page_url: Option<String>,
pub status_page_incident_history_url: Option<String>,
}
impl Default for AppBrandingConfigResponse {
@@ -338,6 +340,8 @@ impl Default for AppBrandingConfigResponse {
wordmark_url: None,
favicon_url: None,
theme_color: None,
status_page_url: None,
status_page_incident_history_url: None,
}
}
}
@@ -858,6 +862,10 @@ pub struct AppBrandingConfigUpdateRequest {
pub favicon_url: Option<Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme_color: Option<Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status_page_url: Option<Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status_page_incident_history_url: Option<Option<String>>,
}
#[derive(Clone, Debug, Default, Serialize)]
+2 -1
View File
@@ -121,6 +121,7 @@ pub async fn render(
admin_user_id: None,
target_id: Some(guild_id.to_owned()),
target_type: Some("guild".to_owned()),
access: Some("write".to_owned()),
sort_by: Some("created_at".to_owned()),
sort_order: Some("desc".to_owned()),
limit: 50,
@@ -134,7 +135,7 @@ pub async fn render(
@if let Some(resp) = resp {
@if resp.logs.is_empty() {
p class="text-sm text-neutral-500" {
"No admin audit log entries for this guild."
"No admin write actions have been recorded for this guild."
}
} @else {
(table_container(table(maud::html! {
+3
View File
@@ -28,6 +28,7 @@ struct AuditLogsQuery {
admin_user_id: Option<String>,
target_id: Option<String>,
target_type: Option<String>,
access: Option<String>,
sort_by: Option<String>,
sort_order: Option<String>,
limit: Option<u32>,
@@ -119,6 +120,7 @@ async fn audit_logs_page(
admin_user_id: query.admin_user_id.as_deref().unwrap_or(""),
target_id: query.target_id.as_deref().unwrap_or(""),
target_type: query.target_type.as_deref().unwrap_or(""),
access: query.access.as_deref().unwrap_or(""),
sort_by: query.sort_by.as_deref().unwrap_or("createdAt"),
sort_order: query.sort_order.as_deref().unwrap_or("desc"),
limit,
@@ -131,6 +133,7 @@ async fn audit_logs_page(
admin_user_id: nonempty(params.admin_user_id),
target_id: nonempty(params.target_id),
target_type: nonempty(params.target_type),
access: nonempty(params.access),
sort_by: Some(params.sort_by.to_owned()),
sort_order: Some(params.sort_order.to_owned()),
limit,
@@ -683,6 +683,8 @@ fn build_app_public_update(form: &MultiValueForm) -> InstanceConfigUpdateRequest
wordmark_url: optional("app_wordmark_url"),
favicon_url: optional("app_favicon_url"),
theme_color: optional("app_theme_color"),
status_page_url: optional("app_status_page_url"),
status_page_incident_history_url: optional("app_status_page_incident_history_url"),
}),
setup: Some(AppSetupConfigUpdateRequest {
configured: Some(form.bool_value("app_setup_configured")),
+1
View File
@@ -245,6 +245,7 @@ pub async fn render(
admin_user_id: None,
target_id: Some(user_id.to_owned()),
target_type: None,
access: Some("write".to_owned()),
sort_by: Some("created_at".to_owned()),
sort_order: Some("desc".to_owned()),
limit,
@@ -72,7 +72,7 @@ pub fn audit_logs_for_target(
let total_pages = total.div_ceil(u64::from(PAGE_SIZE)).max(1);
let page_number = u64::from(current_page) + 1;
let all_logs_href = format!(
"{base_path}/audit-logs?target_id={}",
"{base_path}/audit-logs?target_id={}&access=write",
urlencoding::encode(target_id)
);
@@ -94,7 +94,7 @@ pub fn audit_logs_for_target(
}
}
@if entries.is_empty() {
(empty_state("No admin actions have been recorded against this entity."))
(empty_state("No admin write actions have been recorded against this entity."))
} @else {
(table_container(html! {
(table(html! {
@@ -22,6 +22,7 @@ pub struct AuditLogsParams<'a> {
pub admin_user_id: &'a str,
pub target_id: &'a str,
pub target_type: &'a str,
pub access: &'a str,
pub sort_by: &'a str,
pub sort_order: &'a str,
pub limit: u32,
@@ -42,6 +43,11 @@ fn filters_section(base: &str, params: &AuditLogsParams<'_>) -> Markup {
("file_sha", "File SHA"),
("email", "Email"),
];
let access_options: &[(&str, &str)] = &[
("", "All entries"),
("write", "Writes only"),
("read", "Reads only"),
];
let sort_options: &[(&str, &str)] = &[("createdAt", "Created at"), ("relevance", "Relevance")];
let order_options: &[(&str, &str)] = &[("desc", "Newest first"), ("asc", "Oldest first")];
let limit_options: &[(&str, &str)] =
@@ -60,6 +66,7 @@ fn filters_section(base: &str, params: &AuditLogsParams<'_>) -> Markup {
"Filter by admin user ID..."))
(select_input("target_type", "Target type",
target_type_options, params.target_type))
(select_input("access", "Access", access_options, params.access))
(select_input("sort_by", "Sort by", sort_options, params.sort_by))
(select_input("sort_order", "Order", order_options, params.sort_order))
(select_input("limit", "Page size", limit_options, &limit_str))
@@ -81,6 +88,7 @@ fn build_pagination_url(base: &str, page: u32, params: &AuditLogsParams<'_>) ->
("admin_user_id", params.admin_user_id),
("target_id", params.target_id),
("target_type", params.target_type),
("access", params.access),
("sort_by", params.sort_by),
("sort_order", params.sort_order),
]
@@ -169,6 +177,7 @@ mod tests {
admin_user_id: "",
target_id: "",
target_type: "bulk_job",
access: "",
sort_by: "createdAt",
sort_order: "desc",
limit: 50,
@@ -176,5 +185,28 @@ mod tests {
};
let markup = filters_section("/admin", &params).into_string();
assert!(markup.contains(r#"<option value="bulk_job" selected>Bulk job</option>"#));
assert!(markup.contains(r#"<option value="" selected>All entries</option>"#));
}
#[test]
fn access_filter_survives_form_and_pagination() {
let params = AuditLogsParams {
query: "",
admin_user_id: "",
target_id: "1500000000000000001",
target_type: "",
access: "read",
sort_by: "createdAt",
sort_order: "desc",
limit: 50,
current_page: 0,
};
let markup = filters_section("/admin", &params).into_string();
assert!(markup.contains(r#"<select id="access" name="access""#));
assert!(markup.contains(r#"<option value="read" selected>Reads only</option>"#));
assert_eq!(
build_pagination_url("/admin", 1, &params),
"/admin/audit-logs?page=1&target_id=1500000000000000001&access=read&sort_by=createdAt&sort_order=desc&limit=50"
);
}
}
@@ -848,6 +848,18 @@ fn app_public_config_section(
app_public.branding.favicon_url.as_deref().unwrap_or(""),
"https://example.com/favicon.ico",
))
(text_input(
"app_status_page_url",
"Status page URL",
app_public.branding.status_page_url.as_deref().unwrap_or(""),
"https://fluxerstatus.com",
))
(text_input(
"app_status_page_incident_history_url",
"Status page history URL",
app_public.branding.status_page_incident_history_url.as_deref().unwrap_or(""),
"https://fluxerstatus.com/history",
))
}
div class="space-y-2" {
(checkbox(
+323
View File
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
use fluxer_admin::api::generated::types as generated_types;
use fluxer_admin::api::types;
#[test]
@@ -229,6 +230,7 @@ fn deserialize_audit_logs_response() {
"target_type": "user",
"target_id": "1130958221824557056",
"action": "list_user_sessions",
"access": "read",
"audit_log_reason": null,
"metadata": {"session_count": "3"},
"created_at": "2026-05-26T13:21:47.138Z"
@@ -242,11 +244,332 @@ fn deserialize_audit_logs_response() {
assert_eq!(resp.logs.len(), 1);
assert_eq!(resp.logs[0].log_id, "1508822460457747580");
assert_eq!(resp.logs[0].action, "list_user_sessions");
assert_eq!(resp.logs[0].access.as_deref(), Some("read"));
assert_eq!(resp.logs[0].target_type, "user");
assert!(resp.logs[0].audit_log_reason.is_none());
assert_eq!(resp.logs[0].metadata.get("session_count").unwrap(), "3");
}
#[test]
fn deserialize_guild_audit_logs_with_unknown_keys() {
let json = r#"{
"audit_log_entries": [
{
"id": "1508822460457747581",
"action_type": 22,
"user_id": "1130650140672000000",
"target_id": "1130958221824557056",
"reason": "spam",
"options": {
"delete_message_seconds": 3600,
"future_option": {"nested": true}
},
"changes": [
{"key": "future_change", "future_field": 1}
],
"future_entry_field": "ignored"
},
{
"id": "1508822460457747580",
"action_type": 31,
"user_id": "1130650140672000000",
"target_id": "1130958221824557057",
"options": {
"id": "1130958221824557057",
"type": 0,
"channel_id": "1130958221824557058",
"role_name": "Moderators"
},
"changes": [
{"key": "permissions_diff", "new_value": {"added": ["SEND_MESSAGES"], "removed": []}}
]
},
{
"id": "1508822460457747579",
"action_type": 11,
"user_id": "1130650140672000000",
"target_id": "1130958221824557059",
"options": {"type": 998},
"changes": [
{"key": "name", "old_value": "old-name", "new_value": "new-name"},
{"key": "position", "old_value": 3, "new_value": 4},
{"key": "nsfw", "new_value": true},
{"key": "roles", "new_value": ["1130958221824557060", "1130958221824557061"]}
]
}
],
"users": [],
"webhooks": []
}"#;
let resp: generated_types::ListGuildAuditLogsResponse = serde_json::from_str(json).unwrap();
assert_eq!(resp.audit_log_entries.len(), 3);
let ban = &resp.audit_log_entries[0];
assert_eq!(*ban.action_type, 22);
assert_eq!(ban.reason.as_deref(), Some("spam"));
assert!(ban.options.is_some());
assert_eq!(ban.changes.len(), 1);
assert_eq!(ban.changes[0].key, "future_change");
assert!(ban.changes[0].new_value.is_none());
let role_update = &resp.audit_log_entries[1];
let options = role_update.options.as_ref().unwrap();
assert_eq!(options.role_name.as_deref(), Some("Moderators"));
assert_eq!(options.channel_id.as_deref(), Some("1130958221824557058"));
assert_eq!(options.type_, Some(0.0));
assert_eq!(role_update.changes[0].key, "permissions_diff");
assert!(role_update.changes[0].new_value.is_some());
let channel_update = &resp.audit_log_entries[2];
assert_eq!(channel_update.changes.len(), 4);
assert_eq!(
channel_update.changes[0]
.old_value
.as_ref()
.and_then(|value| value.as_str()),
Some("old-name")
);
assert_eq!(
channel_update.changes[0]
.new_value
.as_ref()
.and_then(|value| value.as_str()),
Some("new-name")
);
assert_eq!(
channel_update.changes[1]
.new_value
.as_ref()
.and_then(|value| value.as_i64()),
Some(4)
);
assert_eq!(
channel_update.changes[2]
.new_value
.as_ref()
.and_then(|value| value.as_bool()),
Some(true)
);
assert_eq!(
channel_update.changes[3]
.new_value
.as_ref()
.and_then(|value| value.as_array())
.map(|value| value.len()),
Some(2)
);
}
#[test]
fn deserialize_instance_config_response_with_unknown_keys() {
let json = r##"{
"sso": {
"enabled": true,
"enforced": false,
"display_name": "Fluxer SSO",
"issuer": "https://id.example.com",
"authorization_url": "https://id.example.com/authorize",
"token_url": "https://id.example.com/token",
"userinfo_url": "https://id.example.com/userinfo",
"jwks_url": "https://id.example.com/jwks.json",
"client_id": "fluxer-admin",
"client_secret_set": true,
"scope": "openid profile email",
"allowed_domains": ["fluxer.com"],
"auto_provision": true,
"redirect_uri": "https://fluxer.com/sso/callback"
},
"gateway_rollout": {
"session_rollout_percentage": 100,
"session_rollout_mode": "modulo",
"guild_rollout_percentage": 100,
"rpc_request_timeout_ms": 10000,
"max_concurrent_session_starts": 512,
"max_concurrent_guild_starts": 256,
"gateway_dispatch_relay_shards": 32,
"gateway_dispatch_relay_max_queue": 50000,
"voice_e2ee_scope": "guild_feature_only",
"future_rollout_knob": 3
},
"voice_noise_suppression": {
"enabled": true,
"config_version": 4,
"default_backend": "rnnoise",
"enabled_backends": ["none", "standard", "rnnoise"],
"allow_user_override": true,
"rollout_basis_points": 10000,
"rollout_salt": "voice-ns-v1",
"included_user_ids": [],
"excluded_user_ids": [],
"guild_overrides": [],
"stereo_enabled": false,
"suppression_strength": 80,
"future_presentation_knob": "verbose",
"future_knob": 7,
"future_object_knob": {"nested": true},
"future_list_knob": ["a", "b"]
},
"experiment_delivery": {"poll_interval_seconds": 300, "poll_jitter_percent": 15},
"registration": {
"mode": "open",
"admin_registration_urls_enabled": false,
"urls": [],
"pending_registrations": []
},
"self_hosted": false,
"app_public": {
"branding": {
"product_name": "Fluxer",
"icon_url": "https://cdn.example.com/icon.png",
"symbol_url": "https://cdn.example.com/symbol.svg",
"logo_url": "https://cdn.example.com/logo.svg",
"wordmark_url": "https://cdn.example.com/wordmark.svg",
"favicon_url": "https://cdn.example.com/favicon.ico",
"theme_color": "#5865f2",
"future_asset_url": "https://cdn.example.com/future.png"
},
"setup": {"configured": true},
"legal": {
"terms_url": "https://fluxer.com/terms",
"privacy_url": "https://fluxer.com/privacy"
},
"registration": {"collect_date_of_birth": true}
},
"policy": {
"single_community_enabled": false,
"single_community_guild_id": null,
"direct_messages_disabled": false,
"direct_messages_locked": false,
"premium_mode": "mirror",
"services": {
"gif_enabled": true,
"youtube_enabled": true,
"bluesky_enabled": false,
"future_service_enabled": true
},
"services_resolved": {
"gif_enabled": true,
"youtube_enabled": true,
"bluesky_enabled": false
},
"services_available": {"gif": true, "youtube": true, "bluesky": false},
"deferred_phone_gate": {
"enabled": false,
"window_hours": 24,
"member_threshold": 100
}
},
"integrations": {
"gif": {"klipy_api_key_set": true, "effective_available": true},
"youtube": {"api_key_set": true, "effective_available": true},
"captcha": {
"provider": "hcaptcha",
"effective_provider": "hcaptcha",
"hcaptcha_site_key": "site",
"hcaptcha_secret_key_set": true,
"turnstile_site_key": "",
"turnstile_secret_key_set": false,
"effective_enabled": true
},
"email": {
"enabled": true,
"effective_enabled": true,
"provider": "smtp",
"effective_provider": "smtp",
"from_email": "[email protected]",
"from_name": "Fluxer",
"smtp": {
"host": "smtp.example.com",
"port": 587,
"username": "fluxer",
"password_set": true,
"secure": true
},
"disable_new_ip_authorization": false,
"effective_disable_new_ip_authorization": false
},
"bluesky": {
"enabled": false,
"effective_enabled": false,
"client_name": "Fluxer",
"client_uri": "https://fluxer.com",
"logo_uri": "https://cdn.example.com/logo.svg",
"tos_uri": "https://fluxer.com/terms",
"policy_uri": "https://fluxer.com/privacy",
"key_count": 0
}
},
"media": {
"attachment_decay": {
"enabled": true,
"min_size_mb": 10,
"max_size_mb": 500,
"max_eligible_size_mb": 500,
"min_lifetime_days": 30,
"max_lifetime_days": 365,
"curve": 2,
"renew_threshold_days": 7,
"renew_window_days": 14,
"effective": {
"enabled": true,
"min_size_mb": 10,
"max_size_mb": 500,
"max_eligible_size_mb": 500,
"min_lifetime_days": 30,
"max_lifetime_days": 365,
"curve": 2,
"renew_threshold_days": 7,
"renew_window_days": 14,
"future_curve": 1.5
}
}
},
"future_section": {"enabled": true, "rollout_basis_points": 10000},
"future_flag": 3
}"##;
let resp: generated_types::InstanceConfigResponse = serde_json::from_str(json).expect(
"the build.rs relaxation must keep unknown response keys from failing the whole response",
);
assert!(!resp.self_hosted);
assert!(resp.voice_noise_suppression.enabled);
assert_eq!(resp.voice_noise_suppression.config_version, 4);
assert_eq!(resp.voice_noise_suppression.rollout_basis_points, 10000);
assert_eq!(*resp.voice_noise_suppression.rollout_salt, "voice-ns-v1");
assert_eq!(resp.voice_noise_suppression.enabled_backends.len(), 3);
assert_eq!(resp.experiment_delivery.poll_interval_seconds, 300);
assert!(resp.policy.single_community_guild_id.is_none());
assert_eq!(resp.policy.services.gif_enabled, Some(true));
assert_eq!(resp.app_public.branding.product_name, "Fluxer");
assert!(resp.media.attachment_decay.effective.enabled);
let without_unknown_keys = json
.replace("\"future_rollout_knob\": 3,", "")
.replace("\"future_presentation_knob\": \"verbose\",", "")
.replace("\"future_knob\": 7,", "")
.replace("\"future_object_knob\": {\"nested\": true},", "")
.replace("\"future_list_knob\": [\"a\", \"b\"],", "")
.replace(
"\"future_asset_url\": \"https://cdn.example.com/future.png\",",
"",
)
.replace("\"future_service_enabled\": true,", "")
.replace("\"future_curve\": 1.5,", "")
.replace(
"\"future_section\": {\"enabled\": true, \"rollout_basis_points\": 10000},",
"",
)
.replace("\"future_flag\": 3,", "");
let baseline: generated_types::InstanceConfigResponse =
serde_json::from_str(&without_unknown_keys).expect("known keys alone still deserialize");
assert_eq!(
serde_json::to_value(&baseline).unwrap(),
serde_json::to_value(&resp).unwrap()
);
}
#[test]
fn deserialize_search_reports_response() {
let json = r#"{
+78
View File
@@ -168,6 +168,39 @@ async fn detail_tab_routes_return_layout_or_fragments_by_route_shape() {
}
}
#[tokio::test]
async fn target_audit_log_tabs_request_write_entries_only() {
let app = setup().await;
for path in [
"/users/1500000000000000001/tabs/audit_logs",
"/guilds/1600000000000000001/tabs/audit_logs",
] {
let fragment = get(&app, path, &[]).await;
assert!(fragment.contains("Temp ban"), "{path}\n{fragment}");
assert!(!fragment.contains("Get user"), "{path}\n{fragment}");
}
}
#[tokio::test]
async fn audit_log_page_forwards_the_access_filter() {
let app = setup().await;
let all = get(&app, "/audit-logs", &[]).await;
assert!(all.contains("Temp ban"), "{all}");
assert!(all.contains("Get user"), "{all}");
assert!(
all.contains(r#"<option value="" selected>All entries</option>"#),
"{all}"
);
let reads = get(&app, "/audit-logs?access=read", &[]).await;
assert!(reads.contains("Get user"), "{reads}");
assert!(!reads.contains("Temp ban"), "{reads}");
assert!(
reads.contains(r#"<option value="read" selected>Reads only</option>"#),
"{reads}"
);
}
#[tokio::test]
async fn report_routes_keep_layout_and_fragment_contract() {
let app = setup().await;
@@ -844,6 +877,25 @@ async fn mock_api(method: Method, uri: Uri) -> Response {
json_response(instance_config_without_pending_registrations())
}
(Method::GET, "/admin/limit-config") => json_response(limit_config()),
(Method::GET, "/admin/audit-logs") => {
let access = uri.query().and_then(|query| {
url::form_urlencoded::parse(query.as_bytes())
.find(|(key, _)| key == "access")
.map(|(_, value)| value.into_owned())
});
let logs = [
audit_log_entry("1900000000000000101", "get_user", "read"),
audit_log_entry("1900000000000000102", "temp_ban", "write"),
]
.into_iter()
.filter(|entry| {
access
.as_deref()
.is_none_or(|access| entry.access.to_string() == access)
})
.collect::<Vec<_>>();
json_response(json!({ "total": logs.len(), "logs": logs }))
}
_ => (StatusCode::NOT_FOUND, Json(json!({ "error": "not found" }))).into_response(),
}
}
@@ -975,6 +1027,32 @@ fn guild_fixtures_match_generated_response_contracts() {
assert_eq!(detail.member_count, 12);
}
fn audit_log_entry(
log_id: &str,
action: &str,
access: &str,
) -> generated_types::AdminAuditLogResponseSchema {
serde_json::from_value(json!({
"log_id": log_id,
"admin_user_id": "1500000000000000000",
"admin_user": null,
"target_type": "user",
"target_id": "1500000000000000001",
"target_user": null,
"target_guild": null,
"target_channel": null,
"related_users": {},
"related_guilds": {},
"related_channels": {},
"action": action,
"access": access,
"audit_log_reason": null,
"metadata": {},
"created_at": "2026-09-16T12:00:00.000Z"
}))
.expect("audit log fixture must match the generated response contract")
}
fn searched_application() -> Value {
json!({
"id": "1700000000000000001",
@@ -6,7 +6,7 @@ import type {AuditLogSearchFilters, SearchableAuditLog} from '@fluxer/schema/src
import type {ElasticsearchDistributedLock} from '@pkgs/elasticsearch_search/src/adapters/ElasticsearchIndexAdapter';
import {ElasticsearchIndexAdapter} from '@pkgs/elasticsearch_search/src/adapters/ElasticsearchIndexAdapter';
import type {ElasticsearchFilter} from '@pkgs/elasticsearch_search/src/ElasticsearchFilterUtils';
import {compactFilters, esTermFilter} from '@pkgs/elasticsearch_search/src/ElasticsearchFilterUtils';
import {compactFilters, esTermFilter, esTermsFilter} from '@pkgs/elasticsearch_search/src/ElasticsearchFilterUtils';
import {ELASTICSEARCH_INDEX_DEFINITIONS} from '@pkgs/elasticsearch_search/src/ElasticsearchIndexDefinitions';
function buildAuditLogFilters(filters: AuditLogSearchFilters): Array<ElasticsearchFilter | undefined> {
@@ -15,6 +15,10 @@ function buildAuditLogFilters(filters: AuditLogSearchFilters): Array<Elasticsear
if (filters.targetType) clauses.push(esTermFilter('targetType', filters.targetType));
if (filters.targetId) clauses.push(esTermFilter('targetId', filters.targetId));
if (filters.action) clauses.push(esTermFilter('action', filters.action));
if (filters.actions && filters.actions.length > 0) clauses.push(esTermsFilter('action.keyword', filters.actions));
if (filters.excludeActions && filters.excludeActions.length > 0) {
clauses.push({bool: {must_not: [esTermsFilter('action.keyword', filters.excludeActions)]}});
}
return compactFilters(clauses);
}
+8 -1
View File
@@ -9,6 +9,13 @@ import type {IEmailService} from '@pkgs/email/src/IEmailService';
import {ms} from 'itty-time';
const logger = createLogger('email-service');
const DEFAULT_LOCALE = 'en-US';
function formatMinorUnitAmount(amountMinor: number, currency: string, locale: string | null): string {
const formatter = new Intl.NumberFormat(locale || DEFAULT_LOCALE, {style: 'currency', currency});
const fractionDigits = formatter.resolvedOptions().maximumFractionDigits ?? 2;
return formatter.format(amountMinor / 10 ** fractionDigits);
}
export class EmailService implements IEmailService {
private readonly config: EmailConfig;
@@ -301,7 +308,7 @@ export class EmailService implements IEmailService {
locale: string | null = null,
): Promise<boolean> {
return this.sendTemplatedEmail(email, 'donation_confirmation', locale, {
amount: (amountCents / 100).toFixed(2),
amount: formatMinorUnitAmount(amountCents, currency, locale),
currency: currency.toUpperCase(),
interval,
manageUrl,
@@ -17,7 +17,7 @@ export const EMAIL_I18N_MESSAGES = {
},
donation_confirmation: {
subject: 'Thank you for your {product_name} donation',
body: 'Hello,\n\nThank you for your donation to {product_name}! Your {interval, select,\n month {recurring donation}\n year {recurring donation}\n other {one-time donation}\n} has been {interval, select,\n month {set up}\n year {set up}\n other {processed}\n} successfully.\n\nDonation details:\nAmount: {amount} {currency} {interval, select,\n month {per month}\n year {per year}\n other {}\n}\n\nStripe will email you a separate receipt with your invoice PDF shortly. This includes all payment details and can be used for tax purposes.\n\nYou can view your donation history, download invoices, {interval, select,\n month {and manage or cancel your subscription}\n year {and manage or cancel your subscription}\n other {and manage future donations}\n} at any time using this link:\n\n{manageUrl}\n\nYour support helps keep {product_name} running. Thank you!\n\n– {product_name} Team',
body: 'Hello,\n\nThank you for your donation to {product_name}! Your {interval, select,\n month {recurring donation}\n year {recurring donation}\n other {one-time donation}\n} has been {interval, select,\n month {set up}\n year {set up}\n other {processed}\n} successfully.\n\nDonation details:\nAmount: {amount} {interval, select,\n month {per month}\n year {per year}\n other {}\n}\n\nStripe will email you a separate receipt with your invoice PDF shortly. This includes all payment details and can be used for tax purposes.\n\nYou can view your donation history, download invoices, {interval, select,\n month {and manage or cancel your subscription}\n year {and manage or cancel your subscription}\n other {and manage future donations}\n} at any time using this link:\n\n{manageUrl}\n\nYour support helps keep {product_name} running. Thank you!\n\n– {product_name} Team',
},
donation_magic_link: {
subject: 'Manage your {product_name} donations',
@@ -17,7 +17,7 @@ const EMAIL_I18N_AR_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "شكرًا لك على تبرعك لـ{product_name}",
"body": "مرحبًا،\n\nشكرًا لك على تبرعك لـ{product_name}! لقد تم {interval, select,\n month {إعداد تبرعك المتكرر}\n year {إعداد تبرعك المتكرر}\n other {معالجة تبرعك لمرة واحدة}\n} بنجاح.\n\nتفاصيل التبرع:\nالمبلغ: {amount} {currency} {interval, select,\n month {شهريًا}\n year {سنويًا}\n other {}\n}\n\nسترسل لك Stripe إيصالًا منفصلًا عبر البريد الإلكتروني مع فاتورتك بصيغة PDF قريبًا. يتضمن هذا جميع تفاصيل الدفع ويمكن استخدامه لأغراض ضريبية.\n\nيمكنك عرض سجل تبرعاتك، وتنزيل الفواتير، {interval, select,\n month {وإدارة أو إلغاء اشتراكك}\n year {وإدارة أو إلغاء اشتراكك}\n other {وإدارة التبرعات المستقبلية}\n} في أي وقت باستخدام هذا الرابط:\n\n{manageUrl}\n\nدعمك يساعد على استمرار {product_name}. شكرًا لك!\n\n– فريق {product_name}"
"body": "مرحبًا،\n\nشكرًا لك على تبرعك لـ{product_name}! لقد تم {interval, select,\n month {إعداد تبرعك المتكرر}\n year {إعداد تبرعك المتكرر}\n other {معالجة تبرعك لمرة واحدة}\n} بنجاح.\n\nتفاصيل التبرع:\nالمبلغ: {amount} {interval, select,\n month {شهريًا}\n year {سنويًا}\n other {}\n}\n\nسترسل لك Stripe إيصالًا منفصلًا عبر البريد الإلكتروني مع فاتورتك بصيغة PDF قريبًا. يتضمن هذا جميع تفاصيل الدفع ويمكن استخدامه لأغراض ضريبية.\n\nيمكنك عرض سجل تبرعاتك، وتنزيل الفواتير، {interval, select,\n month {وإدارة أو إلغاء اشتراكك}\n year {وإدارة أو إلغاء اشتراكك}\n other {وإدارة التبرعات المستقبلية}\n} في أي وقت باستخدام هذا الرابط:\n\n{manageUrl}\n\nدعمك يساعد على استمرار {product_name}. شكرًا لك!\n\n– فريق {product_name}"
},
"donation_magic_link": {
"subject": "إدارة تبرعاتك لـ{product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_BG_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Благодарим ти за дарението към {product_name}",
"body": "Здравей,\n\nБлагодарим ти за дарението към {product_name}! Твоето {interval, select,\n month {периодично дарение}\n year {периодично дарение}\n other {еднократно дарение}\n} беше {interval, select,\n month {настроено}\n year {настроено}\n other {обработено}\n} успешно.\n\nПодробности за дарението:\nСума: {amount} {currency} {interval, select,\n month {на месец}\n year {на година}\n other {}\n}\n\nСкоро Stripe ще ти изпрати по имейл отделна разписка с PDF на фактурата ти. Тя включва всички данни за плащане и може да се използва за данъчни цели.\n\nМожеш да преглеждаш историята на даренията си, да изтегляш фактури {interval, select,\n month {и да управляваш или анулираш абонамента си}\n year {и да управляваш или анулираш абонамента си}\n other {и да управляваш бъдещи дарения}\n} по всяко време, като използваш този линк:\n\n{manageUrl}\n\nТвоята подкрепа помага на {product_name} да продължи да работи. Благодарим ти!\n\n– Екип на {product_name}"
"body": "Здравей,\n\nБлагодарим ти за дарението към {product_name}! Твоето {interval, select,\n month {периодично дарение}\n year {периодично дарение}\n other {еднократно дарение}\n} беше {interval, select,\n month {настроено}\n year {настроено}\n other {обработено}\n} успешно.\n\nПодробности за дарението:\nСума: {amount} {interval, select,\n month {на месец}\n year {на година}\n other {}\n}\n\nСкоро Stripe ще ти изпрати по имейл отделна разписка с PDF на фактурата ти. Тя включва всички данни за плащане и може да се използва за данъчни цели.\n\nМожеш да преглеждаш историята на даренията си, да изтегляш фактури {interval, select,\n month {и да управляваш или анулираш абонамента си}\n year {и да управляваш или анулираш абонамента си}\n other {и да управляваш бъдещи дарения}\n} по всяко време, като използваш този линк:\n\n{manageUrl}\n\nТвоята подкрепа помага на {product_name} да продължи да работи. Благодарим ти!\n\n– Екип на {product_name}"
},
"donation_magic_link": {
"subject": "Управлявай даренията си за {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_CS_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Děkujeme za váš dar pro {product_name}",
"body": "Dobrý den,\n\nDěkujeme za váš dar pro {product_name}! Váš {interval, select,\n month {opakovaný dar}\n year {opakovaný dar}\n other {jednorázový dar}\n} byl úspěšně {interval, select,\n month {nastaven}\n year {nastaven}\n other {zpracován}\n}.\n\nPodrobnosti o daru:\nČástka: {amount} {currency} {interval, select,\n month {měsíčně}\n year {ročně}\n other {}\n}\n\nStripe vám brzy pošle samostatné potvrzení platby s fakturou v PDF. Obsahuje všechny platební údaje a lze ho použít pro daňové účely.\n\nPomocí tohoto odkazu si můžete kdykoli prohlédnout historii svých darů, stáhnout faktury {interval, select,\n month {a spravovat nebo zrušit předplatné}\n year {a spravovat nebo zrušit předplatné}\n other {a spravovat budoucí dary}\n}:\n\n{manageUrl}\n\nVaše podpora pomáhá udržovat {product_name} v chodu. Děkujeme!\n\n– Tým {product_name}"
"body": "Dobrý den,\n\nDěkujeme za váš dar pro {product_name}! Váš {interval, select,\n month {opakovaný dar}\n year {opakovaný dar}\n other {jednorázový dar}\n} byl úspěšně {interval, select,\n month {nastaven}\n year {nastaven}\n other {zpracován}\n}.\n\nPodrobnosti o daru:\nČástka: {amount} {interval, select,\n month {měsíčně}\n year {ročně}\n other {}\n}\n\nStripe vám brzy pošle samostatné potvrzení platby s fakturou v PDF. Obsahuje všechny platební údaje a lze ho použít pro daňové účely.\n\nPomocí tohoto odkazu si můžete kdykoli prohlédnout historii svých darů, stáhnout faktury {interval, select,\n month {a spravovat nebo zrušit předplatné}\n year {a spravovat nebo zrušit předplatné}\n other {a spravovat budoucí dary}\n}:\n\n{manageUrl}\n\nVaše podpora pomáhá udržovat {product_name} v chodu. Děkujeme!\n\n– Tým {product_name}"
},
"donation_magic_link": {
"subject": "Spravujte své dary pro {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_DA_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Tak for din {product_name}-donation",
"body": "Hej,\n\nTak for din donation til {product_name}! Din {interval, select,\n month {løbende donation}\n year {løbende donation}\n other {engangsdonation}\n} er blevet {interval, select,\n month {oprettet}\n year {oprettet}\n other {gennemført}\n}.\n\nDonationsdetaljer:\nBeløb: {amount} {currency} {interval, select,\n month {pr. måned}\n year {pr. år}\n other {}\n}\n\nStripe sender dig en separat kvittering med din faktura som PDF inden længe. Kvitteringen indeholder alle betalingsdetaljer og kan bruges til skatteformål.\n\nDu kan til enhver tid se din donationshistorik, downloade fakturaer {interval, select,\n month {og administrere eller opsige dit abonnement}\n year {og administrere eller opsige dit abonnement}\n other {og administrere fremtidige donationer}\n} ved at bruge dette link:\n\n{manageUrl}\n\nDin støtte hjælper med at holde {product_name} kørende. Tak!\n\n– Teamet bag {product_name}"
"body": "Hej,\n\nTak for din donation til {product_name}! Din {interval, select,\n month {løbende donation}\n year {løbende donation}\n other {engangsdonation}\n} er blevet {interval, select,\n month {oprettet}\n year {oprettet}\n other {gennemført}\n}.\n\nDonationsdetaljer:\nBeløb: {amount} {interval, select,\n month {pr. måned}\n year {pr. år}\n other {}\n}\n\nStripe sender dig en separat kvittering med din faktura som PDF inden længe. Kvitteringen indeholder alle betalingsdetaljer og kan bruges til skatteformål.\n\nDu kan til enhver tid se din donationshistorik, downloade fakturaer {interval, select,\n month {og administrere eller opsige dit abonnement}\n year {og administrere eller opsige dit abonnement}\n other {og administrere fremtidige donationer}\n} ved at bruge dette link:\n\n{manageUrl}\n\nDin støtte hjælper med at holde {product_name} kørende. Tak!\n\n– Teamet bag {product_name}"
},
"donation_magic_link": {
"subject": "Administrer dine {product_name}-donationer",
@@ -17,7 +17,7 @@ const EMAIL_I18N_DE_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Vielen Dank für deine {product_name}-Spende",
"body": "Hallo,\n\nvielen Dank für deine Spende an {product_name}! Deine {interval, select,\n month {wiederkehrende Spende}\n year {wiederkehrende Spende}\n other {einmalige Spende}\n} wurde erfolgreich {interval, select,\n month {eingerichtet}\n year {eingerichtet}\n other {verarbeitet}\n}.\n\nDetails zur Spende:\nBetrag: {amount} {currency} {interval, select,\n month {pro Monat}\n year {pro Jahr}\n other {}\n}\n\nStripe sendet dir in Kürze eine separate Quittung mit deiner Rechnungs-PDF per E-Mail. Diese enthält alle Zahlungsdetails und kann für Steuerzwecke verwendet werden.\n\nÜber diesen Link kannst du jederzeit deine Spendenübersicht aufrufen, Rechnungen herunterladen {interval, select,\n month {und dein Abonnement verwalten oder kündigen}\n year {und dein Abonnement verwalten oder kündigen}\n other {und zukünftige Spenden verwalten}\n}:\n\n{manageUrl}\n\nDeine Unterstützung hilft, {product_name} am Laufen zu halten. Vielen Dank!\n\n– {product_name}-Team"
"body": "Hallo,\n\nvielen Dank für deine Spende an {product_name}! Deine {interval, select,\n month {wiederkehrende Spende}\n year {wiederkehrende Spende}\n other {einmalige Spende}\n} wurde erfolgreich {interval, select,\n month {eingerichtet}\n year {eingerichtet}\n other {verarbeitet}\n}.\n\nDetails zur Spende:\nBetrag: {amount} {interval, select,\n month {pro Monat}\n year {pro Jahr}\n other {}\n}\n\nStripe sendet dir in Kürze eine separate Quittung mit deiner Rechnungs-PDF per E-Mail. Diese enthält alle Zahlungsdetails und kann für Steuerzwecke verwendet werden.\n\nÜber diesen Link kannst du jederzeit deine Spendenübersicht aufrufen, Rechnungen herunterladen {interval, select,\n month {und dein Abonnement verwalten oder kündigen}\n year {und dein Abonnement verwalten oder kündigen}\n other {und zukünftige Spenden verwalten}\n}:\n\n{manageUrl}\n\nDeine Unterstützung hilft, {product_name} am Laufen zu halten. Vielen Dank!\n\n– {product_name}-Team"
},
"donation_magic_link": {
"subject": "Verwalte deine {product_name}-Spenden",
@@ -17,7 +17,7 @@ const EMAIL_I18N_EL_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Ευχαριστούμε για τη δωρεά σου στο {product_name}",
"body": "Γεια σου,\n\nΕυχαριστούμε για τη δωρεά σου στο {product_name}! Η {interval, select,\n month {επαναλαμβανόμενη δωρεά}\n year {επαναλαμβανόμενη δωρεά}\n other {εφάπαξ δωρεά}\n} σου έχει {interval, select,\n month {ρυθμιστεί}\n year {ρυθμιστεί}\n other {ολοκληρωθεί}\n} με επιτυχία.\n\nΛεπτομέρειες δωρεάς:\nΠοσό: {amount} {currency} {interval, select,\n month {ανά μήνα}\n year {ανά έτος}\n other {}\n}\n\nΗ Stripe θα σου στείλει σύντομα ξεχωριστή απόδειξη με το PDF του τιμολογίου σου. Περιλαμβάνει όλες τις λεπτομέρειες πληρωμής και μπορεί να χρησιμοποιηθεί για φορολογικούς σκοπούς.\n\nΜπορείς να δεις το ιστορικό των δωρεών σου, να κατεβάσεις τιμολόγια, {interval, select,\n month {και να διαχειριστείς ή να ακυρώσεις τη συνδρομή σου}\n year {και να διαχειριστείς ή να ακυρώσεις τη συνδρομή σου}\n other {και να διαχειριστείς μελλοντικές δωρεές}\n} ανά πάσα στιγμή χρησιμοποιώντας αυτόν τον σύνδεσμο:\n\n{manageUrl}\n\nΗ υποστήριξή σου βοηθάει το {product_name} να συνεχίσει να λειτουργεί. Ευχαριστούμε!\n\n– Η ομάδα του {product_name}"
"body": "Γεια σου,\n\nΕυχαριστούμε για τη δωρεά σου στο {product_name}! Η {interval, select,\n month {επαναλαμβανόμενη δωρεά}\n year {επαναλαμβανόμενη δωρεά}\n other {εφάπαξ δωρεά}\n} σου έχει {interval, select,\n month {ρυθμιστεί}\n year {ρυθμιστεί}\n other {ολοκληρωθεί}\n} με επιτυχία.\n\nΛεπτομέρειες δωρεάς:\nΠοσό: {amount} {interval, select,\n month {ανά μήνα}\n year {ανά έτος}\n other {}\n}\n\nΗ Stripe θα σου στείλει σύντομα ξεχωριστή απόδειξη με το PDF του τιμολογίου σου. Περιλαμβάνει όλες τις λεπτομέρειες πληρωμής και μπορεί να χρησιμοποιηθεί για φορολογικούς σκοπούς.\n\nΜπορείς να δεις το ιστορικό των δωρεών σου, να κατεβάσεις τιμολόγια, {interval, select,\n month {και να διαχειριστείς ή να ακυρώσεις τη συνδρομή σου}\n year {και να διαχειριστείς ή να ακυρώσεις τη συνδρομή σου}\n other {και να διαχειριστείς μελλοντικές δωρεές}\n} ανά πάσα στιγμή χρησιμοποιώντας αυτόν τον σύνδεσμο:\n\n{manageUrl}\n\nΗ υποστήριξή σου βοηθάει το {product_name} να συνεχίσει να λειτουργεί. Ευχαριστούμε!\n\n– Η ομάδα του {product_name}"
},
"donation_magic_link": {
"subject": "Διαχειρίσου τις δωρεές σου στο {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_EN_GB_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Thank you for your {product_name} donation",
"body": "Hello,\n\nThank you for your donation to {product_name}! Your {interval, select,\n month {recurring donation}\n year {recurring donation}\n other {one-time donation}\n} has been {interval, select,\n month {set up}\n year {set up}\n other {processed}\n} successfully.\n\nDonation details:\nAmount: {amount} {currency} {interval, select,\n month {per month}\n year {per year}\n other {}\n}\n\nStripe will email you a separate receipt with your invoice PDF shortly. This includes all payment details and can be used for tax purposes.\n\nYou can view your donation history, download invoices, {interval, select,\n month {and manage or cancel your subscription}\n year {and manage or cancel your subscription}\n other {and manage future donations}\n} at any time using this link:\n\n{manageUrl}\n\nYour support helps keep {product_name} running. Thank you!\n\n– {product_name} Team"
"body": "Hello,\n\nThank you for your donation to {product_name}! Your {interval, select,\n month {recurring donation}\n year {recurring donation}\n other {one-time donation}\n} has been {interval, select,\n month {set up}\n year {set up}\n other {processed}\n} successfully.\n\nDonation details:\nAmount: {amount} {interval, select,\n month {per month}\n year {per year}\n other {}\n}\n\nStripe will email you a separate receipt with your invoice PDF shortly. This includes all payment details and can be used for tax purposes.\n\nYou can view your donation history, download invoices, {interval, select,\n month {and manage or cancel your subscription}\n year {and manage or cancel your subscription}\n other {and manage future donations}\n} at any time using this link:\n\n{manageUrl}\n\nYour support helps keep {product_name} running. Thank you!\n\n– {product_name} Team"
},
"donation_magic_link": {
"subject": "Manage your {product_name} donations",
@@ -17,7 +17,7 @@ const EMAIL_I18N_ES_419_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Gracias por tu donación a {product_name}",
"body": "Hola:\n\n¡Gracias por tu donación a {product_name}! Tu {interval, select,\n month {donación recurrente}\n year {donación recurrente}\n other {donación única}\n} ha sido {interval, select,\n month {configurada}\n year {configurada}\n other {procesada}\n} con éxito.\n\nDetalles de la donación:\nMonto: {amount} {currency} {interval, select,\n month {por mes}\n year {por año}\n other {}\n}\n\nEn breve, Stripe te enviará por separado un correo electrónico con el recibo y la factura en PDF. Este recibo incluye todos los detalles de pago y se puede utilizar para fines fiscales.\n\nPuedes ver tu historial de donaciones, descargar facturas {interval, select,\n month {y administrar o cancelar tu suscripción}\n year {y administrar o cancelar tu suscripción}\n other {y administrar futuras donaciones}\n} en cualquier momento usando este enlace:\n\n{manageUrl}\n\nTu apoyo ayuda a mantener {product_name} en funcionamiento. ¡Gracias!\n\n– Equipo de {product_name}"
"body": "Hola:\n\n¡Gracias por tu donación a {product_name}! Tu {interval, select,\n month {donación recurrente}\n year {donación recurrente}\n other {donación única}\n} ha sido {interval, select,\n month {configurada}\n year {configurada}\n other {procesada}\n} con éxito.\n\nDetalles de la donación:\nMonto: {amount} {interval, select,\n month {por mes}\n year {por año}\n other {}\n}\n\nEn breve, Stripe te enviará por separado un correo electrónico con el recibo y la factura en PDF. Este recibo incluye todos los detalles de pago y se puede utilizar para fines fiscales.\n\nPuedes ver tu historial de donaciones, descargar facturas {interval, select,\n month {y administrar o cancelar tu suscripción}\n year {y administrar o cancelar tu suscripción}\n other {y administrar futuras donaciones}\n} en cualquier momento usando este enlace:\n\n{manageUrl}\n\nTu apoyo ayuda a mantener {product_name} en funcionamiento. ¡Gracias!\n\n– Equipo de {product_name}"
},
"donation_magic_link": {
"subject": "Administra tus donaciones a {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_ES_ES_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Gracias por tu donación a {product_name}",
"body": "Hola:\n\n¡Gracias por tu donación a {product_name}! Tu {interval, select,\n month {donación recurrente}\n year {donación recurrente}\n other {donación única}\n} se ha {interval, select,\n month {configurado}\n year {configurado}\n other {procesado}\n} correctamente.\n\nDetalles de la donación:\nImporte: {amount} {currency} {interval, select,\n month {al mes}\n year {al año}\n other {}\n}\n\nEn breve, Stripe te enviará por separado un correo electrónico con el recibo y la factura en PDF. Este recibo incluye todos los detalles de pago y se puede utilizar para fines fiscales.\n\nPuedes ver tu historial de donaciones, descargar facturas {interval, select,\n month {y gestionar o cancelar tu suscripción}\n year {y gestionar o cancelar tu suscripción}\n other {y gestionar futuras donaciones}\n} en cualquier momento usando este enlace:\n\n{manageUrl}\n\nTu apoyo ayuda a mantener {product_name} en funcionamiento. ¡Gracias!\n\n– Equipo de {product_name}"
"body": "Hola:\n\n¡Gracias por tu donación a {product_name}! Tu {interval, select,\n month {donación recurrente}\n year {donación recurrente}\n other {donación única}\n} se ha {interval, select,\n month {configurado}\n year {configurado}\n other {procesado}\n} correctamente.\n\nDetalles de la donación:\nImporte: {amount} {interval, select,\n month {al mes}\n year {al año}\n other {}\n}\n\nEn breve, Stripe te enviará por separado un correo electrónico con el recibo y la factura en PDF. Este recibo incluye todos los detalles de pago y se puede utilizar para fines fiscales.\n\nPuedes ver tu historial de donaciones, descargar facturas {interval, select,\n month {y gestionar o cancelar tu suscripción}\n year {y gestionar o cancelar tu suscripción}\n other {y gestionar futuras donaciones}\n} en cualquier momento usando este enlace:\n\n{manageUrl}\n\nTu apoyo ayuda a mantener {product_name} en funcionamiento. ¡Gracias!\n\n– Equipo de {product_name}"
},
"donation_magic_link": {
"subject": "Gestiona tus donaciones a {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_FI_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Kiitos {product_name}-lahjoituksestasi",
"body": "Hei,\n\nKiitos {product_name}-lahjoituksestasi! {interval, select,\n month {Toistuva lahjoituksesi}\n year {Toistuva lahjoituksesi}\n other {Kertalahjoituksesi}\n} on {interval, select,\n month {käynnistetty}\n year {käynnistetty}\n other {käsitelty}\n} onnistuneesti.\n\nLahjoituksen tiedot:\nSumma: {amount} {currency} {interval, select,\n month {kuukaudessa}\n year {vuodessa}\n other {}\n}\n\nStripe lähettää sinulle pian sähköpostitse erillisen kuitin sekä PDF-laskun. Niissä ovat kaikki maksutiedot, ja niitä voi käyttää verotuksessa.\n\nVoit tarkastella lahjoitushistoriaasi, ladata laskuja {interval, select,\n month {ja hallinnoida tai peruuttaa tilaustasi}\n year {ja hallinnoida tai peruuttaa tilaustasi}\n other {ja hallita tulevia lahjoituksia}\n} milloin tahansa käyttämällä tätä linkkiä:\n\n{manageUrl}\n\nTukesi auttaa pitämään {product_name}-palvelun toiminnassa. Kiitos!\n\n– {product_name}-tiimi"
"body": "Hei,\n\nKiitos {product_name}-lahjoituksestasi! {interval, select,\n month {Toistuva lahjoituksesi}\n year {Toistuva lahjoituksesi}\n other {Kertalahjoituksesi}\n} on {interval, select,\n month {käynnistetty}\n year {käynnistetty}\n other {käsitelty}\n} onnistuneesti.\n\nLahjoituksen tiedot:\nSumma: {amount} {interval, select,\n month {kuukaudessa}\n year {vuodessa}\n other {}\n}\n\nStripe lähettää sinulle pian sähköpostitse erillisen kuitin sekä PDF-laskun. Niissä ovat kaikki maksutiedot, ja niitä voi käyttää verotuksessa.\n\nVoit tarkastella lahjoitushistoriaasi, ladata laskuja {interval, select,\n month {ja hallinnoida tai peruuttaa tilaustasi}\n year {ja hallinnoida tai peruuttaa tilaustasi}\n other {ja hallita tulevia lahjoituksia}\n} milloin tahansa käyttämällä tätä linkkiä:\n\n{manageUrl}\n\nTukesi auttaa pitämään {product_name}-palvelun toiminnassa. Kiitos!\n\n– {product_name}-tiimi"
},
"donation_magic_link": {
"subject": "Hallitse {product_name}-lahjoituksiasi",
@@ -17,7 +17,7 @@ const EMAIL_I18N_FR_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Merci pour votre don à {product_name}",
"body": "Bonjour,\n\nMerci pour votre don à {product_name} ! {interval, select, month {Votre don récurrent a bien été mis en place.} year {Votre don récurrent a bien été mis en place.} other {Votre don ponctuel a bien été traité.}}\n\nDétails du don :\nMontant : {amount} {currency} {interval, select, month {par mois} year {par an} other {}}\n\nStripe vous enverra bientôt un reçu séparé accompagné de votre facture PDF. Ce document contient tous les détails du paiement et peut servir de justificatif fiscal.\n\nVous pouvez consulter l’historique de vos dons, télécharger des factures {interval, select, month {et gérer ou résilier votre abonnement} year {et gérer ou résilier votre abonnement} other {et gérer vos futurs dons}} à tout moment avec ce lien :\n\n{manageUrl}\n\nVotre soutien contribue au fonctionnement de {product_name}. Merci !\n\n– L’équipe {product_name}"
"body": "Bonjour,\n\nMerci pour votre don à {product_name} ! {interval, select, month {Votre don récurrent a bien été mis en place.} year {Votre don récurrent a bien été mis en place.} other {Votre don ponctuel a bien été traité.}}\n\nDétails du don :\nMontant : {amount} {interval, select, month {par mois} year {par an} other {}}\n\nStripe vous enverra bientôt un reçu séparé accompagné de votre facture PDF. Ce document contient tous les détails du paiement et peut servir de justificatif fiscal.\n\nVous pouvez consulter l’historique de vos dons, télécharger des factures {interval, select, month {et gérer ou résilier votre abonnement} year {et gérer ou résilier votre abonnement} other {et gérer vos futurs dons}} à tout moment avec ce lien :\n\n{manageUrl}\n\nVotre soutien contribue au fonctionnement de {product_name}. Merci !\n\n– L’équipe {product_name}"
},
"donation_magic_link": {
"subject": "Gérer vos dons à {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_HE_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "תודה על תרומתכם ל-{product_name}",
"body": "שלום,\n\nתודה על תרומתכם ל-{product_name}! ה{interval, select,\n month {תרומה החוזרת}\n year {תרומה החוזרת}\n other {תרומה החד-פעמית}\n} שלכם {interval, select,\n month {הוגדרה}\n year {הוגדרה}\n other {בוצעה}\n} בהצלחה.\n\nפרטי התרומה:\nסכום: {amount} {currency} {interval, select,\n month {לחודש}\n year {לשנה}\n other {}\n}\n\nStripe ישלח לכם בקרוב קבלה נפרדת עם קובץ PDF של החשבונית. הקבלה תכלול את כל פרטי התשלום וניתן יהיה להשתמש בה למטרות מס.\n\nאתם יכולים לצפות בהיסטוריית התרומות שלכם, להוריד חשבוניות, {interval, select,\n month {ולנהל או לבטל את המנוי}\n year {ולנהל או לבטל את המנוי}\n other {ולנהל תרומות עתידיות}\n} בכל עת באמצעות קישור זה:\n\n{manageUrl}\n\nהתמיכה שלכם עוזרת לשמור על פעילות {product_name}. תודה!\n\nצוות {product_name}"
"body": "שלום,\n\nתודה על תרומתכם ל-{product_name}! ה{interval, select,\n month {תרומה החוזרת}\n year {תרומה החוזרת}\n other {תרומה החד-פעמית}\n} שלכם {interval, select,\n month {הוגדרה}\n year {הוגדרה}\n other {בוצעה}\n} בהצלחה.\n\nפרטי התרומה:\nסכום: {amount} {interval, select,\n month {לחודש}\n year {לשנה}\n other {}\n}\n\nStripe ישלח לכם בקרוב קבלה נפרדת עם קובץ PDF של החשבונית. הקבלה תכלול את כל פרטי התשלום וניתן יהיה להשתמש בה למטרות מס.\n\nאתם יכולים לצפות בהיסטוריית התרומות שלכם, להוריד חשבוניות, {interval, select,\n month {ולנהל או לבטל את המנוי}\n year {ולנהל או לבטל את המנוי}\n other {ולנהל תרומות עתידיות}\n} בכל עת באמצעות קישור זה:\n\n{manageUrl}\n\nהתמיכה שלכם עוזרת לשמור על פעילות {product_name}. תודה!\n\nצוות {product_name}"
},
"donation_magic_link": {
"subject": "ניהול התרומות שלכם ל-{product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_HI_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "आपके {product_name} डोनेशन के लिए धन्यवाद",
"body": "नमस्ते,\n\n{product_name} को आपके डोनेशन के लिए धन्यवाद! आपका {interval, select,\n month {रिकरिंग डोनेशन}\n year {रिकरिंग डोनेशन}\n other {वन-टाइम डोनेशन}\n} सफलतापूर्वक {interval, select,\n month {सेट अप हो गया है}\n year {सेट अप हो गया है}\n other {प्रोसेस हो गया है}\n}।\n\nडोनेशन का विवरण:\nराशि: {amount} {currency} {interval, select,\n month {प्रति माह}\n year {प्रति वर्ष}\n other {}\n}\n\nStripe आपको जल्द ही आपके इनवॉइस PDF के साथ एक अलग रसीद ईमेल करेगा। इसमें पेमेंट की सारी जानकारी शामिल है और इसे टैक्स के लिए इस्तेमाल किया जा सकता है।\n\nआप इस लिंक से किसी भी समय अपने डोनेशन का इतिहास देख सकते हैं, इनवॉइस डाउनलोड कर सकते हैं, {interval, select,\n month {और अपना सब्सक्रिप्शन मैनेज या कैंसल कर सकते हैं}\n year {और अपना सब्सक्रिप्शन मैनेज या कैंसल कर सकते हैं}\n other {और आगे के डोनेशन मैनेज कर सकते हैं}\n}।\n\n{manageUrl}\n\nआपका सपोर्ट {product_name} को चालू रखने में मदद करता है। धन्यवाद!\n\n– {product_name} टीम"
"body": "नमस्ते,\n\n{product_name} को आपके डोनेशन के लिए धन्यवाद! आपका {interval, select,\n month {रिकरिंग डोनेशन}\n year {रिकरिंग डोनेशन}\n other {वन-टाइम डोनेशन}\n} सफलतापूर्वक {interval, select,\n month {सेट अप हो गया है}\n year {सेट अप हो गया है}\n other {प्रोसेस हो गया है}\n}।\n\nडोनेशन का विवरण:\nराशि: {amount} {interval, select,\n month {प्रति माह}\n year {प्रति वर्ष}\n other {}\n}\n\nStripe आपको जल्द ही आपके इनवॉइस PDF के साथ एक अलग रसीद ईमेल करेगा। इसमें पेमेंट की सारी जानकारी शामिल है और इसे टैक्स के लिए इस्तेमाल किया जा सकता है।\n\nआप इस लिंक से किसी भी समय अपने डोनेशन का इतिहास देख सकते हैं, इनवॉइस डाउनलोड कर सकते हैं, {interval, select,\n month {और अपना सब्सक्रिप्शन मैनेज या कैंसल कर सकते हैं}\n year {और अपना सब्सक्रिप्शन मैनेज या कैंसल कर सकते हैं}\n other {और आगे के डोनेशन मैनेज कर सकते हैं}\n}।\n\n{manageUrl}\n\nआपका सपोर्ट {product_name} को चालू रखने में मदद करता है। धन्यवाद!\n\n– {product_name} टीम"
},
"donation_magic_link": {
"subject": "अपने {product_name} डोनेशन मैनेज करें",
@@ -17,7 +17,7 @@ const EMAIL_I18N_HR_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Hvala ti na donaciji za {product_name}",
"body": "Pozdrav,\n\nHvala ti na donaciji za {product_name}! Tvoja {interval, select,\n month {redovita donacija}\n year {redovita donacija}\n other {jednokratna donacija}\n} uspješno je {interval, select,\n month {postavljena}\n year {postavljena}\n other {obrađena}\n}.\n\nDetalji donacije:\nIznos: {amount} {currency} {interval, select,\n month {mjesečno}\n year {godišnje}\n other {}\n}\n\nStripe će ti uskoro poslati zasebnu potvrdu s PDF-om računa. Ona sadrži sve detalje plaćanja i možeš je koristiti u porezne svrhe.\n\nMožeš pregledati povijest donacija, preuzeti račune {interval, select,\n month {i upravljati pretplatom ili je otkazati}\n year {i upravljati pretplatom ili je otkazati}\n other {i upravljati budućim donacijama}\n} u bilo kojem trenutku putem ove poveznice:\n\n{manageUrl}\n\nTvoja podrška pomaže da {product_name} nastavi raditi. Hvala ti!\n\n– Tim {product_name}a"
"body": "Pozdrav,\n\nHvala ti na donaciji za {product_name}! Tvoja {interval, select,\n month {redovita donacija}\n year {redovita donacija}\n other {jednokratna donacija}\n} uspješno je {interval, select,\n month {postavljena}\n year {postavljena}\n other {obrađena}\n}.\n\nDetalji donacije:\nIznos: {amount} {interval, select,\n month {mjesečno}\n year {godišnje}\n other {}\n}\n\nStripe će ti uskoro poslati zasebnu potvrdu s PDF-om računa. Ona sadrži sve detalje plaćanja i možeš je koristiti u porezne svrhe.\n\nMožeš pregledati povijest donacija, preuzeti račune {interval, select,\n month {i upravljati pretplatom ili je otkazati}\n year {i upravljati pretplatom ili je otkazati}\n other {i upravljati budućim donacijama}\n} u bilo kojem trenutku putem ove poveznice:\n\n{manageUrl}\n\nTvoja podrška pomaže da {product_name} nastavi raditi. Hvala ti!\n\n– Tim {product_name}a"
},
"donation_magic_link": {
"subject": "Upravljanje donacijama za {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_HU_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Köszönjük, hogy adományoddal támogatod a {product_name} működését",
"body": "Szia!\n\nKöszönjük, hogy adományoddal támogatod a {product_name} működését! {interval, select,\n month {Sikeresen beállítottuk a rendszeres adományodat.}\n year {Sikeresen beállítottuk a rendszeres adományodat.}\n other {Sikeresen feldolgoztuk az egyszeri adományodat.}\n}\n\nAz adomány részletei:\nÖsszeg: {amount} {currency} {interval, select,\n month {havonta}\n year {évente}\n other {}\n}\n\nA Stripe hamarosan külön e-mailben küld nyugtát és egy PDF-számlát. Ezek tartalmazzák az összes fizetési adatot, és adózási célokra is használhatók.\n\nAz alábbi linken bármikor megtekintheted korábbi adományaidat, letöltheted a számlákat, {interval, select,\n month {valamint kezelheted vagy lemondhatod az előfizetésedet}\n year {valamint kezelheted vagy lemondhatod az előfizetésedet}\n other {valamint kezelheted a jövőbeli adományaidat}\n}:\n\n{manageUrl}\n\nTámogatásod segít fenntartani a {product_name} működését. Köszönjük!\n\n– {product_name} csapata"
"body": "Szia!\n\nKöszönjük, hogy adományoddal támogatod a {product_name} működését! {interval, select,\n month {Sikeresen beállítottuk a rendszeres adományodat.}\n year {Sikeresen beállítottuk a rendszeres adományodat.}\n other {Sikeresen feldolgoztuk az egyszeri adományodat.}\n}\n\nAz adomány részletei:\nÖsszeg: {amount} {interval, select,\n month {havonta}\n year {évente}\n other {}\n}\n\nA Stripe hamarosan külön e-mailben küld nyugtát és egy PDF-számlát. Ezek tartalmazzák az összes fizetési adatot, és adózási célokra is használhatók.\n\nAz alábbi linken bármikor megtekintheted korábbi adományaidat, letöltheted a számlákat, {interval, select,\n month {valamint kezelheted vagy lemondhatod az előfizetésedet}\n year {valamint kezelheted vagy lemondhatod az előfizetésedet}\n other {valamint kezelheted a jövőbeli adományaidat}\n}:\n\n{manageUrl}\n\nTámogatásod segít fenntartani a {product_name} működését. Köszönjük!\n\n– {product_name} csapata"
},
"donation_magic_link": {
"subject": "A {product_name} működését támogató adományaid kezelése",
@@ -17,7 +17,7 @@ const EMAIL_I18N_ID_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Terima kasih atas donasimu untuk {product_name}",
"body": "Halo,\n\nTerima kasih atas donasimu untuk {product_name}! Donasi {interval, select,\n month {berulang}\n year {berulang}\n other {satu kali}\n} kamu telah berhasil {interval, select,\n month {diatur}\n year {diatur}\n other {diproses}\n}.\n\nDetail donasi:\nJumlah: {amount} {currency} {interval, select,\n month {per bulan}\n year {per tahun}\n other {}\n}\n\nStripe akan segera mengirimi kamu email tanda terima terpisah beserta PDF fakturmu. Ini mencakup semua detail pembayaran dan bisa dipakai untuk keperluan pajak.\n\nKamu bisa melihat riwayat donasimu, mengunduh faktur, {interval, select,\n month {dan mengelola atau membatalkan langgananmu}\n year {dan mengelola atau membatalkan langgananmu}\n other {dan mengelola donasi di masa mendatang}\n} kapan saja lewat tautan ini:\n\n{manageUrl}\n\nDukunganmu membantu {product_name} tetap berjalan. Terima kasih!\n\n– Tim {product_name}"
"body": "Halo,\n\nTerima kasih atas donasimu untuk {product_name}! Donasi {interval, select,\n month {berulang}\n year {berulang}\n other {satu kali}\n} kamu telah berhasil {interval, select,\n month {diatur}\n year {diatur}\n other {diproses}\n}.\n\nDetail donasi:\nJumlah: {amount} {interval, select,\n month {per bulan}\n year {per tahun}\n other {}\n}\n\nStripe akan segera mengirimi kamu email tanda terima terpisah beserta PDF fakturmu. Ini mencakup semua detail pembayaran dan bisa dipakai untuk keperluan pajak.\n\nKamu bisa melihat riwayat donasimu, mengunduh faktur, {interval, select,\n month {dan mengelola atau membatalkan langgananmu}\n year {dan mengelola atau membatalkan langgananmu}\n other {dan mengelola donasi di masa mendatang}\n} kapan saja lewat tautan ini:\n\n{manageUrl}\n\nDukunganmu membantu {product_name} tetap berjalan. Terima kasih!\n\n– Tim {product_name}"
},
"donation_magic_link": {
"subject": "Kelola donasimu untuk {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_IT_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Grazie per la tua donazione a {product_name}",
"body": "Ciao,\n\nGrazie per la tua donazione a {product_name}! La tua {interval, select,\n month {donazione ricorrente}\n year {donazione ricorrente}\n other {donazione una tantum}\n} è stata {interval, select,\n month {attivata}\n year {attivata}\n other {elaborata}\n} con successo.\n\nDettagli della donazione:\nImporto: {amount} {currency} {interval, select,\n month {al mese}\n year {all'anno}\n other {}\n}\n\nStripe ti invierà a breve una ricevuta separata con la tua fattura in PDF. Questa include tutti i dettagli del pagamento e può essere utilizzata a fini fiscali.\n\nPuoi visualizzare la cronologia delle tue donazioni, scaricare le fatture {interval, select,\n month {e gestire o disdire il tuo abbonamento}\n year {e gestire o disdire il tuo abbonamento}\n other {e gestire le future donazioni}\n} in qualsiasi momento utilizzando questo link:\n\n{manageUrl}\n\nIl tuo supporto aiuta a mantenere {product_name} in funzione. Grazie!\n\n– Il team di {product_name}"
"body": "Ciao,\n\nGrazie per la tua donazione a {product_name}! La tua {interval, select,\n month {donazione ricorrente}\n year {donazione ricorrente}\n other {donazione una tantum}\n} è stata {interval, select,\n month {attivata}\n year {attivata}\n other {elaborata}\n} con successo.\n\nDettagli della donazione:\nImporto: {amount} {interval, select,\n month {al mese}\n year {all'anno}\n other {}\n}\n\nStripe ti invierà a breve una ricevuta separata con la tua fattura in PDF. Questa include tutti i dettagli del pagamento e può essere utilizzata a fini fiscali.\n\nPuoi visualizzare la cronologia delle tue donazioni, scaricare le fatture {interval, select,\n month {e gestire o disdire il tuo abbonamento}\n year {e gestire o disdire il tuo abbonamento}\n other {e gestire le future donazioni}\n} in qualsiasi momento utilizzando questo link:\n\n{manageUrl}\n\nIl tuo supporto aiuta a mantenere {product_name} in funzione. Grazie!\n\n– Il team di {product_name}"
},
"donation_magic_link": {
"subject": "Gestisci le tue donazioni a {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_JA_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "{product_name}へのご寄付ありがとうございます",
"body": "こんにちは。\n\n{product_name}へのご寄付ありがとうございます。{interval, select,\n month {定期寄付}\n year {定期寄付}\n other {1回のみの寄付}\n}の{interval, select,\n month {設定}\n year {設定}\n other {処理}\n}が完了しました。\n\n寄付の詳細:\n金額: {amount} {currency}{interval, select,\n month {(毎月)}\n year {(毎年)}\n other {}\n}\n\nStripeから、請求書PDF付きの領収書が別途メールで送信されます。これにはすべての支払い詳細が含まれており、税務目的にご利用いただけます。\n\n以下のリンクから、いつでも寄付履歴の表示、請求書のダウンロード、{interval, select,\n month {およびサブスクリプションの管理またはキャンセル}\n year {およびサブスクリプションの管理またはキャンセル}\n other {および今後の寄付の管理}\n}が可能です。\n\n{manageUrl}\n\n皆様のご支援が{product_name}の運営を支えています。ありがとうございます。\n\n– {product_name}チーム"
"body": "こんにちは。\n\n{product_name}へのご寄付ありがとうございます。{interval, select,\n month {定期寄付}\n year {定期寄付}\n other {1回のみの寄付}\n}の{interval, select,\n month {設定}\n year {設定}\n other {処理}\n}が完了しました。\n\n寄付の詳細:\n金額: {amount}{interval, select,\n month {(毎月)}\n year {(毎年)}\n other {}\n}\n\nStripeから、請求書PDF付きの領収書が別途メールで送信されます。これにはすべての支払い詳細が含まれており、税務目的にご利用いただけます。\n\n以下のリンクから、いつでも寄付履歴の表示、請求書のダウンロード、{interval, select,\n month {およびサブスクリプションの管理またはキャンセル}\n year {およびサブスクリプションの管理またはキャンセル}\n other {および今後の寄付の管理}\n}が可能です。\n\n{manageUrl}\n\n皆様のご支援が{product_name}の運営を支えています。ありがとうございます。\n\n– {product_name}チーム"
},
"donation_magic_link": {
"subject": "{product_name}への寄付を管理",
@@ -17,7 +17,7 @@ const EMAIL_I18N_KO_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "{product_name} 후원에 감사드려요",
"body": "안녕하세요,\n\n{product_name}에 후원해 주셔서 감사해요! 회원님의 {interval, select,\n month {정기 후원}\n year {정기 후원}\n other {일회성 후원}\n}이 성공적으로 {interval, select,\n month {설정되었어요}\n year {설정되었어요}\n other {처리되었어요}\n}.\n\n후원 상세 정보:\n금액: {amount} {currency} {interval, select,\n month {매월}\n year {매년}\n other {}\n}\n\nStripe에서 곧 인보이스 PDF가 포함된 별도의 영수증을 이메일로 보내드릴 예정이에요. 이 영수증에는 모든 결제 세부 정보가 포함되어 있으며, 세금 목적으로 활용할 수 있어요.\n\n다음 링크를 통해 언제든지 후원 내역을 확인하고, 인보이스를 다운로드하고, {interval, select,\n month {구독을 관리하거나 취소할 수 있어요}\n year {구독을 관리하거나 취소할 수 있어요}\n other {향후 후원을 관리할 수 있어요}\n}:\n\n{manageUrl}\n\n회원님의 후원은 {product_name}를 계속 운영하는 데 큰 도움이 돼요. 진심으로 감사해요!\n\n– {product_name} 팀"
"body": "안녕하세요,\n\n{product_name}에 후원해 주셔서 감사해요! 회원님의 {interval, select,\n month {정기 후원}\n year {정기 후원}\n other {일회성 후원}\n}이 성공적으로 {interval, select,\n month {설정되었어요}\n year {설정되었어요}\n other {처리되었어요}\n}.\n\n후원 상세 정보:\n금액: {amount} {interval, select,\n month {매월}\n year {매년}\n other {}\n}\n\nStripe에서 곧 인보이스 PDF가 포함된 별도의 영수증을 이메일로 보내드릴 예정이에요. 이 영수증에는 모든 결제 세부 정보가 포함되어 있으며, 세금 목적으로 활용할 수 있어요.\n\n다음 링크를 통해 언제든지 후원 내역을 확인하고, 인보이스를 다운로드하고, {interval, select,\n month {구독을 관리하거나 취소할 수 있어요}\n year {구독을 관리하거나 취소할 수 있어요}\n other {향후 후원을 관리할 수 있어요}\n}:\n\n{manageUrl}\n\n회원님의 후원은 {product_name}를 계속 운영하는 데 큰 도움이 돼요. 진심으로 감사해요!\n\n– {product_name} 팀"
},
"donation_magic_link": {
"subject": "{product_name} 후원 내역 관리",
@@ -17,7 +17,7 @@ const EMAIL_I18N_LT_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Dėkojame už tavo {product_name} paramą",
"body": "Sveiki,\n\nDėkojame už tavo paramą {product_name}! Tavo {interval, select,\n month {pasikartojanti parama}\n year {pasikartojanti parama}\n other {vienkartinė parama}\n} sėkmingai {interval, select,\n month {nustatyta}\n year {nustatyta}\n other {apdorota}\n}.\n\nParamos informacija:\nSuma: {amount} {currency} {interval, select,\n month {per mėnesį}\n year {per metus}\n other {}\n}\n\nStripe netrukus atsiųs tau atskirą kvitą su sąskaitos faktūros PDF. Jame nurodyta visa mokėjimo informacija ir jį galima naudoti mokesčių tikslais.\n\nBet kuriuo metu šia nuoroda gali peržiūrėti savo paramos istoriją, atsisiųsti sąskaitas faktūras {interval, select,\n month {ir valdyti arba atšaukti savo prenumeratą}\n year {ir valdyti arba atšaukti savo prenumeratą}\n other {ir valdyti būsimą paramą}\n}:\n\n{manageUrl}\n\nTavo parama padeda {product_name} veikti. Ačiū!\n\n– {product_name} komanda"
"body": "Sveiki,\n\nDėkojame už tavo paramą {product_name}! Tavo {interval, select,\n month {pasikartojanti parama}\n year {pasikartojanti parama}\n other {vienkartinė parama}\n} sėkmingai {interval, select,\n month {nustatyta}\n year {nustatyta}\n other {apdorota}\n}.\n\nParamos informacija:\nSuma: {amount} {interval, select,\n month {per mėnesį}\n year {per metus}\n other {}\n}\n\nStripe netrukus atsiųs tau atskirą kvitą su sąskaitos faktūros PDF. Jame nurodyta visa mokėjimo informacija ir jį galima naudoti mokesčių tikslais.\n\nBet kuriuo metu šia nuoroda gali peržiūrėti savo paramos istoriją, atsisiųsti sąskaitas faktūras {interval, select,\n month {ir valdyti arba atšaukti savo prenumeratą}\n year {ir valdyti arba atšaukti savo prenumeratą}\n other {ir valdyti būsimą paramą}\n}:\n\n{manageUrl}\n\nTavo parama padeda {product_name} veikti. Ačiū!\n\n– {product_name} komanda"
},
"donation_magic_link": {
"subject": "Tvarkyk savo {product_name} paramą",
@@ -17,7 +17,7 @@ const EMAIL_I18N_NL_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Bedankt voor je {product_name}-donatie",
"body": "Hallo,\n\nBedankt voor je donatie aan {product_name}! Je {interval, select,\n month {terugkerende donatie}\n year {terugkerende donatie}\n other {eenmalige donatie}\n} is succesvol {interval, select,\n month {ingesteld}\n year {ingesteld}\n other {verwerkt}\n}.\n\nDonatiegegevens:\nBedrag: {amount} {currency} {interval, select,\n month {per maand}\n year {per jaar}\n other {}\n}\n\nStripe stuurt je binnenkort per e-mail een apart betalingsbewijs met je factuur als pdf. Dit bevat alle betalingsgegevens en kan worden gebruikt voor belastingdoeleinden.\n\nJe kunt op elk moment je donatiegeschiedenis bekijken, facturen downloaden en {interval, select,\n month {je abonnement beheren of opzeggen}\n year {je abonnement beheren of opzeggen}\n other {toekomstige donaties beheren}\n} via deze link:\n\n{manageUrl}\n\nJe steun helpt {product_name} draaiende te houden. Bedankt!\n\n– Het team van {product_name}"
"body": "Hallo,\n\nBedankt voor je donatie aan {product_name}! Je {interval, select,\n month {terugkerende donatie}\n year {terugkerende donatie}\n other {eenmalige donatie}\n} is succesvol {interval, select,\n month {ingesteld}\n year {ingesteld}\n other {verwerkt}\n}.\n\nDonatiegegevens:\nBedrag: {amount} {interval, select,\n month {per maand}\n year {per jaar}\n other {}\n}\n\nStripe stuurt je binnenkort per e-mail een apart betalingsbewijs met je factuur als pdf. Dit bevat alle betalingsgegevens en kan worden gebruikt voor belastingdoeleinden.\n\nJe kunt op elk moment je donatiegeschiedenis bekijken, facturen downloaden en {interval, select,\n month {je abonnement beheren of opzeggen}\n year {je abonnement beheren of opzeggen}\n other {toekomstige donaties beheren}\n} via deze link:\n\n{manageUrl}\n\nJe steun helpt {product_name} draaiende te houden. Bedankt!\n\n– Het team van {product_name}"
},
"donation_magic_link": {
"subject": "Beheer je {product_name}-donaties",
@@ -17,7 +17,7 @@ const EMAIL_I18N_NO_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Takk for bidraget ditt til {product_name}",
"body": "Hei,\n\nTakk for bidraget ditt til {product_name}! {interval, select,\n month {Det faste bidraget ditt er opprettet.}\n year {Det faste bidraget ditt er opprettet.}\n other {Engangsbidraget ditt er behandlet.}\n}\n\nDetaljer om bidraget:\nBeløp: {amount} {currency} {interval, select,\n month {per måned}\n year {per år}\n other {}\n}\n\nStripe sender deg snart en egen kvittering med fakturaen som PDF. Kvitteringen inneholder alle betalingsdetaljer og kan brukes til skatteformål.\n\nDu kan når som helst se bidragshistorikken din, laste ned fakturaer og {interval, select,\n month {administrere eller si opp abonnementet ditt}\n year {administrere eller si opp abonnementet ditt}\n other {administrere framtidige bidrag}\n} via denne lenken:\n\n{manageUrl}\n\nStøtten din bidrar til å holde {product_name} i gang. Takk!\n\n– {product_name}-teamet"
"body": "Hei,\n\nTakk for bidraget ditt til {product_name}! {interval, select,\n month {Det faste bidraget ditt er opprettet.}\n year {Det faste bidraget ditt er opprettet.}\n other {Engangsbidraget ditt er behandlet.}\n}\n\nDetaljer om bidraget:\nBeløp: {amount} {interval, select,\n month {per måned}\n year {per år}\n other {}\n}\n\nStripe sender deg snart en egen kvittering med fakturaen som PDF. Kvitteringen inneholder alle betalingsdetaljer og kan brukes til skatteformål.\n\nDu kan når som helst se bidragshistorikken din, laste ned fakturaer og {interval, select,\n month {administrere eller si opp abonnementet ditt}\n year {administrere eller si opp abonnementet ditt}\n other {administrere framtidige bidrag}\n} via denne lenken:\n\n{manageUrl}\n\nStøtten din bidrar til å holde {product_name} i gang. Takk!\n\n– {product_name}-teamet"
},
"donation_magic_link": {
"subject": "Administrer {product_name}-bidragene dine",
@@ -17,7 +17,7 @@ const EMAIL_I18N_PL_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Dziękujemy za darowiznę dla {product_name}",
"body": "Witaj,\n\nDziękujemy za darowiznę dla {product_name}! Twoja {interval, select,\n month {cykliczna darowizna}\n year {cykliczna darowizna}\n other {jednorazowa darowizna}\n} została {interval, select,\n month {uruchomiona}\n year {uruchomiona}\n other {przetworzona}\n} pomyślnie.\n\nSzczegóły darowizny:\nKwota: {amount} {currency} {interval, select,\n month {miesięcznie}\n year {rocznie}\n other {}\n}\n\nStripe wkrótce wyśle Ci osobną wiadomość e-mail z potwierdzeniem i fakturą w formacie PDF. Potwierdzenie zawiera wszystkie szczegóły płatności i może służyć do celów podatkowych.\n\nMożesz przeglądać historię swoich darowizn, pobierać faktury {interval, select,\n month {oraz zarządzać subskrypcją lub ją anulować}\n year {oraz zarządzać subskrypcją lub ją anulować}\n other {oraz zarządzać przyszłymi darowiznami}\n} w dowolnym momencie, korzystając z tego linku:\n\n{manageUrl}\n\nTwoje wsparcie pomaga utrzymać {product_name}. Dziękujemy!\n\n– Zespół {product_name}"
"body": "Witaj,\n\nDziękujemy za darowiznę dla {product_name}! Twoja {interval, select,\n month {cykliczna darowizna}\n year {cykliczna darowizna}\n other {jednorazowa darowizna}\n} została {interval, select,\n month {uruchomiona}\n year {uruchomiona}\n other {przetworzona}\n} pomyślnie.\n\nSzczegóły darowizny:\nKwota: {amount} {interval, select,\n month {miesięcznie}\n year {rocznie}\n other {}\n}\n\nStripe wkrótce wyśle Ci osobną wiadomość e-mail z potwierdzeniem i fakturą w formacie PDF. Potwierdzenie zawiera wszystkie szczegóły płatności i może służyć do celów podatkowych.\n\nMożesz przeglądać historię swoich darowizn, pobierać faktury {interval, select,\n month {oraz zarządzać subskrypcją lub ją anulować}\n year {oraz zarządzać subskrypcją lub ją anulować}\n other {oraz zarządzać przyszłymi darowiznami}\n} w dowolnym momencie, korzystając z tego linku:\n\n{manageUrl}\n\nTwoje wsparcie pomaga utrzymać {product_name}. Dziękujemy!\n\n– Zespół {product_name}"
},
"donation_magic_link": {
"subject": "Zarządzaj darowiznami dla {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_PT_BR_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Agradecemos pela sua doação ao {product_name}",
"body": "Olá,\n\nAgradecemos pela sua doação ao {product_name}! Sua {interval, select,\n month {doação recorrente}\n year {doação recorrente}\n other {doação única}\n} foi {interval, select,\n month {configurada}\n year {configurada}\n other {processada}\n} com sucesso.\n\nDetalhes da doação:\nValor: {amount} {currency} {interval, select,\n month {por mês}\n year {por ano}\n other {}\n}\n\nA Stripe enviará um recibo separado com sua fatura em PDF em breve. Isso inclui todos os detalhes de pagamento e pode ser usado para fins fiscais.\n\nVocê pode visualizar seu histórico de doações, baixar faturas {interval, select,\n month {e gerenciar ou cancelar sua assinatura}\n year {e gerenciar ou cancelar sua assinatura}\n other {e gerenciar futuras doações}\n} a qualquer momento usando este link:\n\n{manageUrl}\n\nSeu apoio ajuda a manter o {product_name} funcionando. Agradecemos!\n\n– Equipe do {product_name}"
"body": "Olá,\n\nAgradecemos pela sua doação ao {product_name}! Sua {interval, select,\n month {doação recorrente}\n year {doação recorrente}\n other {doação única}\n} foi {interval, select,\n month {configurada}\n year {configurada}\n other {processada}\n} com sucesso.\n\nDetalhes da doação:\nValor: {amount} {interval, select,\n month {por mês}\n year {por ano}\n other {}\n}\n\nA Stripe enviará um recibo separado com sua fatura em PDF em breve. Isso inclui todos os detalhes de pagamento e pode ser usado para fins fiscais.\n\nVocê pode visualizar seu histórico de doações, baixar faturas {interval, select,\n month {e gerenciar ou cancelar sua assinatura}\n year {e gerenciar ou cancelar sua assinatura}\n other {e gerenciar futuras doações}\n} a qualquer momento usando este link:\n\n{manageUrl}\n\nSeu apoio ajuda a manter o {product_name} funcionando. Agradecemos!\n\n– Equipe do {product_name}"
},
"donation_magic_link": {
"subject": "Gerencie suas doações ao {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_RO_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Îți mulțumim pentru donația către {product_name}",
"body": "Salut,\n\nÎți mulțumim pentru donația ta către {product_name}! {interval, select,\n month {Donația ta recurentă}\n year {Donația ta recurentă}\n other {Donația ta unică}\n} a fost {interval, select,\n month {configurată}\n year {configurată}\n other {procesată}\n} cu succes.\n\nDetalii donație:\nSumă: {amount} {currency} {interval, select,\n month {pe lună}\n year {pe an}\n other {}\n}\n\nStripe îți va trimite în scurt timp un e-mail separat cu chitanța și factura ta în format PDF. Acestea includ toate detaliile plății și pot fi folosite în scopuri fiscale.\n\nPoți vizualiza istoricul donațiilor, descărca facturi {interval, select,\n month {și gestiona sau anula abonamentul}\n year {și gestiona sau anula abonamentul}\n other {și gestiona donațiile viitoare}\n} în orice moment folosind acest link:\n\n{manageUrl}\n\nSprijinul tău ne ajută să menținem {product_name} în funcțiune. Mulțumim!\n\n– Echipa {product_name}"
"body": "Salut,\n\nÎți mulțumim pentru donația ta către {product_name}! {interval, select,\n month {Donația ta recurentă}\n year {Donația ta recurentă}\n other {Donația ta unică}\n} a fost {interval, select,\n month {configurată}\n year {configurată}\n other {procesată}\n} cu succes.\n\nDetalii donație:\nSumă: {amount} {interval, select,\n month {pe lună}\n year {pe an}\n other {}\n}\n\nStripe îți va trimite în scurt timp un e-mail separat cu chitanța și factura ta în format PDF. Acestea includ toate detaliile plății și pot fi folosite în scopuri fiscale.\n\nPoți vizualiza istoricul donațiilor, descărca facturi {interval, select,\n month {și gestiona sau anula abonamentul}\n year {și gestiona sau anula abonamentul}\n other {și gestiona donațiile viitoare}\n} în orice moment folosind acest link:\n\n{manageUrl}\n\nSprijinul tău ne ajută să menținem {product_name} în funcțiune. Mulțumim!\n\n– Echipa {product_name}"
},
"donation_magic_link": {
"subject": "Gestionează-ți donațiile către {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_RU_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Спасибо за твоё пожертвование для {product_name}",
"body": "Привет,\n\nСпасибо за твоё пожертвование для {product_name}! Твоё {interval, select,\n month {регулярное пожертвование}\n year {регулярное пожертвование}\n other {разовое пожертвование}\n} успешно {interval, select,\n month {настроено}\n year {настроено}\n other {обработано}\n}.\n\nДетали пожертвования:\nСумма: {amount} {currency} {interval, select,\n month {в месяц}\n year {в год}\n other {}\n}\n\nStripe вскоре отправит тебе отдельную квитанцию со счётом в формате PDF. В ней есть все платёжные данные, и её можно использовать для налоговых целей.\n\nТы можешь просматривать историю своих пожертвований, скачивать счета, {interval, select,\n month {а также управлять подпиской или отменить её}\n year {а также управлять подпиской или отменить её}\n other {и управлять будущими пожертвованиями}\n} в любое время по этой ссылке:\n\n{manageUrl}\n\nТвоя поддержка помогает {product_name} работать. Спасибо!\n\n– Команда {product_name}"
"body": "Привет,\n\nСпасибо за твоё пожертвование для {product_name}! Твоё {interval, select,\n month {регулярное пожертвование}\n year {регулярное пожертвование}\n other {разовое пожертвование}\n} успешно {interval, select,\n month {настроено}\n year {настроено}\n other {обработано}\n}.\n\nДетали пожертвования:\nСумма: {amount} {interval, select,\n month {в месяц}\n year {в год}\n other {}\n}\n\nStripe вскоре отправит тебе отдельную квитанцию со счётом в формате PDF. В ней есть все платёжные данные, и её можно использовать для налоговых целей.\n\nТы можешь просматривать историю своих пожертвований, скачивать счета, {interval, select,\n month {а также управлять подпиской или отменить её}\n year {а также управлять подпиской или отменить её}\n other {и управлять будущими пожертвованиями}\n} в любое время по этой ссылке:\n\n{manageUrl}\n\nТвоя поддержка помогает {product_name} работать. Спасибо!\n\n– Команда {product_name}"
},
"donation_magic_link": {
"subject": "Управляй своими пожертвованиями для {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_SV_SE_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Tack för din donation till {product_name}",
"body": "Hej,\n\nTack för din donation till {product_name}! {interval, select,\n month {Din återkommande donation har registrerats.}\n year {Din återkommande donation har registrerats.}\n other {Din engångsdonation har genomförts.}\n}\n\nDonationsuppgifter:\nBelopp: {amount} {currency} {interval, select,\n month {per månad}\n year {per år}\n other {}\n}\n\nStripe skickar snart ett separat kvitto med din faktura i PDF-format via e-post. Där finns alla betalningsuppgifter, och underlaget kan användas för skatteändamål.\n\nDu kan se din donationshistorik, ladda ner fakturor {interval, select,\n month {och hantera eller avsluta din återkommande donation}\n year {och hantera eller avsluta din återkommande donation}\n other {och hantera framtida donationer}\n} när som helst via den här länken:\n\n{manageUrl}\n\nDitt stöd hjälper till att hålla {product_name} igång. Tack!\n\n– {product_name}-teamet"
"body": "Hej,\n\nTack för din donation till {product_name}! {interval, select,\n month {Din återkommande donation har registrerats.}\n year {Din återkommande donation har registrerats.}\n other {Din engångsdonation har genomförts.}\n}\n\nDonationsuppgifter:\nBelopp: {amount} {interval, select,\n month {per månad}\n year {per år}\n other {}\n}\n\nStripe skickar snart ett separat kvitto med din faktura i PDF-format via e-post. Där finns alla betalningsuppgifter, och underlaget kan användas för skatteändamål.\n\nDu kan se din donationshistorik, ladda ner fakturor {interval, select,\n month {och hantera eller avsluta din återkommande donation}\n year {och hantera eller avsluta din återkommande donation}\n other {och hantera framtida donationer}\n} när som helst via den här länken:\n\n{manageUrl}\n\nDitt stöd hjälper till att hålla {product_name} igång. Tack!\n\n– {product_name}-teamet"
},
"donation_magic_link": {
"subject": "Hantera dina donationer till {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_TH_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "ขอบคุณสำหรับการบริจาคให้ {product_name}",
"body": "สวัสดี,\n\nขอบคุณสำหรับการบริจาคให้ {product_name}! {interval, select,\n month {การบริจาคแบบต่อเนื่อง}\n year {การบริจาคแบบต่อเนื่อง}\n other {การบริจาคครั้งเดียว}\n}ของคุณได้รับการ{interval, select,\n month {ตั้งค่า}\n year {ตั้งค่า}\n other {ดำเนินการ}\n}เรียบร้อยแล้ว\n\nรายละเอียดการบริจาค:\nจำนวนเงิน: {amount} {currency} {interval, select,\n month {ต่อเดือน}\n year {ต่อปี}\n other {}\n}\n\nStripe จะส่งอีเมลใบเสร็จแยกต่างหากพร้อมไฟล์ PDF ใบแจ้งหนี้ให้คุณในไม่ช้า ใบเสร็จนี้มีรายละเอียดการชำระเงินทั้งหมดและใช้สำหรับวัตถุประสงค์ทางภาษีได้\n\nคุณสามารถดูประวัติการบริจาค ดาวน์โหลดใบแจ้งหนี้ {interval, select,\n month {และจัดการหรือยกเลิกการสมัครสมาชิกของคุณ}\n year {และจัดการหรือยกเลิกการสมัครสมาชิกของคุณ}\n other {และจัดการการบริจาคในอนาคต}\n} ได้ตลอดเวลาโดยใช้ลิงก์นี้:\n\n{manageUrl}\n\nการสนับสนุนของคุณช่วยให้ {product_name} ดำเนินต่อไปได้ ขอบคุณ!\n\n– ทีม {product_name}"
"body": "สวัสดี,\n\nขอบคุณสำหรับการบริจาคให้ {product_name}! {interval, select,\n month {การบริจาคแบบต่อเนื่อง}\n year {การบริจาคแบบต่อเนื่อง}\n other {การบริจาคครั้งเดียว}\n}ของคุณได้รับการ{interval, select,\n month {ตั้งค่า}\n year {ตั้งค่า}\n other {ดำเนินการ}\n}เรียบร้อยแล้ว\n\nรายละเอียดการบริจาค:\nจำนวนเงิน: {amount} {interval, select,\n month {ต่อเดือน}\n year {ต่อปี}\n other {}\n}\n\nStripe จะส่งอีเมลใบเสร็จแยกต่างหากพร้อมไฟล์ PDF ใบแจ้งหนี้ให้คุณในไม่ช้า ใบเสร็จนี้มีรายละเอียดการชำระเงินทั้งหมดและใช้สำหรับวัตถุประสงค์ทางภาษีได้\n\nคุณสามารถดูประวัติการบริจาค ดาวน์โหลดใบแจ้งหนี้ {interval, select,\n month {และจัดการหรือยกเลิกการสมัครสมาชิกของคุณ}\n year {และจัดการหรือยกเลิกการสมัครสมาชิกของคุณ}\n other {และจัดการการบริจาคในอนาคต}\n} ได้ตลอดเวลาโดยใช้ลิงก์นี้:\n\n{manageUrl}\n\nการสนับสนุนของคุณช่วยให้ {product_name} ดำเนินต่อไปได้ ขอบคุณ!\n\n– ทีม {product_name}"
},
"donation_magic_link": {
"subject": "จัดการการบริจาคให้ {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_TR_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "{product_name}'a yaptığın bağış için teşekkürler",
"body": "Merhaba,\n\n{product_name}'a yaptığın bağış için teşekkürler! {interval, select,\n month {tekrarlayan bağışın}\n year {tekrarlayan bağışın}\n other {tek seferlik bağışın}\n} başarıyla {interval, select,\n month {ayarlandı}\n year {ayarlandı}\n other {işlendi}\n}.\n\nBağış detayları:\nTutar: {amount} {currency} {interval, select,\n month {aylık}\n year {yıllık}\n other {}\n}\n\nStripe, kısa süre içinde fatura PDF'ini içeren ayrı bir makbuzu sana e-posta ile gönderecektir. Bu, tüm ödeme detaylarını içerir ve vergi amaçlı kullanılabilir.\n\nBu bağlantıyı kullanarak istediğin zaman bağış geçmişini görüntüleyebilir, faturaları indirebilir, {interval, select,\n month {ve aboneliğini yönetebilir veya iptal edebilirsin}\n year {ve aboneliğini yönetebilir veya iptal edebilirsin}\n other {ve gelecekteki bağışlarını yönetebilirsin}\n}:\n\n{manageUrl}\n\nDesteğin {product_name}'ın çalışmaya devam etmesine yardımcı oluyor. Teşekkürler!\n\n– {product_name} Ekibi"
"body": "Merhaba,\n\n{product_name}'a yaptığın bağış için teşekkürler! {interval, select,\n month {tekrarlayan bağışın}\n year {tekrarlayan bağışın}\n other {tek seferlik bağışın}\n} başarıyla {interval, select,\n month {ayarlandı}\n year {ayarlandı}\n other {işlendi}\n}.\n\nBağış detayları:\nTutar: {amount} {interval, select,\n month {aylık}\n year {yıllık}\n other {}\n}\n\nStripe, kısa süre içinde fatura PDF'ini içeren ayrı bir makbuzu sana e-posta ile gönderecektir. Bu, tüm ödeme detaylarını içerir ve vergi amaçlı kullanılabilir.\n\nBu bağlantıyı kullanarak istediğin zaman bağış geçmişini görüntüleyebilir, faturaları indirebilir, {interval, select,\n month {ve aboneliğini yönetebilir veya iptal edebilirsin}\n year {ve aboneliğini yönetebilir veya iptal edebilirsin}\n other {ve gelecekteki bağışlarını yönetebilirsin}\n}:\n\n{manageUrl}\n\nDesteğin {product_name}'ın çalışmaya devam etmesine yardımcı oluyor. Teşekkürler!\n\n– {product_name} Ekibi"
},
"donation_magic_link": {
"subject": "{product_name} bağışlarını yönet",
@@ -17,7 +17,7 @@ const EMAIL_I18N_UK_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Дякуємо за твою пожертву для {product_name}",
"body": "Привіт,\n\nДякуємо за твою пожертву для {product_name}! Твою {interval, select,\n month {регулярну пожертву}\n year {регулярну пожертву}\n other {одноразову пожертву}\n} успішно {interval, select,\n month {налаштовано}\n year {налаштовано}\n other {оброблено}\n}.\n\nДеталі пожертви:\nСума: {amount} {currency} {interval, select,\n month {на місяць}\n year {на рік}\n other {}\n}\n\nStripe незабаром надішле тобі окрему квитанцію з рахунком у форматі PDF. Вона містить усі платіжні дані, і її можна використати для податкових цілей.\n\nТи можеш переглядати історію своїх пожертв, завантажувати рахунки, {interval, select,\n month {а також керувати підпискою або скасувати її}\n year {а також керувати підпискою або скасувати її}\n other {а також керувати майбутніми пожертвами}\n} у будь-який час за цим посиланням:\n\n{manageUrl}\n\nТвоя підтримка допомагає {product_name} працювати. Дякуємо!\n\n– Команда {product_name}"
"body": "Привіт,\n\nДякуємо за твою пожертву для {product_name}! Твою {interval, select,\n month {регулярну пожертву}\n year {регулярну пожертву}\n other {одноразову пожертву}\n} успішно {interval, select,\n month {налаштовано}\n year {налаштовано}\n other {оброблено}\n}.\n\nДеталі пожертви:\nСума: {amount} {interval, select,\n month {на місяць}\n year {на рік}\n other {}\n}\n\nStripe незабаром надішле тобі окрему квитанцію з рахунком у форматі PDF. Вона містить усі платіжні дані, і її можна використати для податкових цілей.\n\nТи можеш переглядати історію своїх пожертв, завантажувати рахунки, {interval, select,\n month {а також керувати підпискою або скасувати її}\n year {а також керувати підпискою або скасувати її}\n other {а також керувати майбутніми пожертвами}\n} у будь-який час за цим посиланням:\n\n{manageUrl}\n\nТвоя підтримка допомагає {product_name} працювати. Дякуємо!\n\n– Команда {product_name}"
},
"donation_magic_link": {
"subject": "Керуй своїми пожертвами для {product_name}",
@@ -17,7 +17,7 @@ const EMAIL_I18N_VI_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "Cảm ơn bạn đã ủng hộ {product_name}",
"body": "Xin chào,\n\nCảm ơn bạn đã ủng hộ {product_name}! {interval, select,\n month {Khoản ủng hộ định kỳ}\n year {Khoản ủng hộ định kỳ}\n other {Khoản ủng hộ một lần}\n} của bạn đã được {interval, select,\n month {thiết lập}\n year {thiết lập}\n other {xử lý}\n} thành công.\n\nChi tiết ủng hộ:\nSố tiền: {amount} {currency} {interval, select,\n month {mỗi tháng}\n year {mỗi năm}\n other {}\n}\n\nStripe sẽ sớm gửi cho bạn một email biên lai riêng kèm hóa đơn PDF. Biên lai này gồm đầy đủ chi tiết thanh toán và có thể dùng cho mục đích thuế.\n\nBạn có thể xem lịch sử ủng hộ, tải xuống hóa đơn, {interval, select,\n month {và quản lý hoặc hủy gói đăng ký}\n year {và quản lý hoặc hủy gói đăng ký}\n other {và quản lý các khoản ủng hộ trong tương lai}\n} bất cứ lúc nào bằng cách sử dụng liên kết này:\n\n{manageUrl}\n\nSự hỗ trợ của bạn giúp {product_name} tiếp tục hoạt động. Cảm ơn bạn!\n\n– Đội ngũ {product_name}"
"body": "Xin chào,\n\nCảm ơn bạn đã ủng hộ {product_name}! {interval, select,\n month {Khoản ủng hộ định kỳ}\n year {Khoản ủng hộ định kỳ}\n other {Khoản ủng hộ một lần}\n} của bạn đã được {interval, select,\n month {thiết lập}\n year {thiết lập}\n other {xử lý}\n} thành công.\n\nChi tiết ủng hộ:\nSố tiền: {amount} {interval, select,\n month {mỗi tháng}\n year {mỗi năm}\n other {}\n}\n\nStripe sẽ sớm gửi cho bạn một email biên lai riêng kèm hóa đơn PDF. Biên lai này gồm đầy đủ chi tiết thanh toán và có thể dùng cho mục đích thuế.\n\nBạn có thể xem lịch sử ủng hộ, tải xuống hóa đơn, {interval, select,\n month {và quản lý hoặc hủy gói đăng ký}\n year {và quản lý hoặc hủy gói đăng ký}\n other {và quản lý các khoản ủng hộ trong tương lai}\n} bất cứ lúc nào bằng cách sử dụng liên kết này:\n\n{manageUrl}\n\nSự hỗ trợ của bạn giúp {product_name} tiếp tục hoạt động. Cảm ơn bạn!\n\n– Đội ngũ {product_name}"
},
"donation_magic_link": {
"subject": "Quản lý các khoản ủng hộ {product_name} của bạn",
@@ -17,7 +17,7 @@ const EMAIL_I18N_ZH_CN_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "感谢你的 {product_name} 捐赠",
"body": "你好,\n\n感谢你对 {product_name} 的捐赠!你的{interval, select,\n month {定期捐赠}\n year {定期捐赠}\n other {一次性捐赠}\n}{interval, select,\n month {已成功设置}\n year {已成功设置}\n other {已成功处理}\n}。\n\n捐赠详情:\n金额:{amount} {currency} {interval, select,\n month {每月}\n year {每年}\n other {}\n}\n\nStripe 稍后会另外发送一封收据邮件,其中附有发票 PDF。邮件包含所有付款详情,可用于报税。\n\n你可以随时使用此链接查看你的捐赠历史、下载发票,以及{interval, select,\n month {管理或取消你的订阅}\n year {管理或取消你的订阅}\n other {管理你的未来捐赠}\n}:\n\n{manageUrl}\n\n你的支持有助于 {product_name} 的持续运行。谢谢!\n\n– {product_name} 团队"
"body": "你好,\n\n感谢你对 {product_name} 的捐赠!你的{interval, select,\n month {定期捐赠}\n year {定期捐赠}\n other {一次性捐赠}\n}{interval, select,\n month {已成功设置}\n year {已成功设置}\n other {已成功处理}\n}。\n\n捐赠详情:\n金额:{amount} {interval, select,\n month {每月}\n year {每年}\n other {}\n}\n\nStripe 稍后会另外发送一封收据邮件,其中附有发票 PDF。邮件包含所有付款详情,可用于报税。\n\n你可以随时使用此链接查看你的捐赠历史、下载发票,以及{interval, select,\n month {管理或取消你的订阅}\n year {管理或取消你的订阅}\n other {管理你的未来捐赠}\n}:\n\n{manageUrl}\n\n你的支持有助于 {product_name} 的持续运行。谢谢!\n\n– {product_name} 团队"
},
"donation_magic_link": {
"subject": "管理你的 {product_name} 捐赠",
@@ -17,7 +17,7 @@ const EMAIL_I18N_ZH_TW_MESSAGES = defineEmailI18nLocaleMessages({
},
"donation_confirmation": {
"subject": "感謝您對 {product_name} 的捐款",
"body": "哈囉,\n\n感謝您對 {product_name} 的捐款!您的{interval, select,\n month {定期捐款}\n year {定期捐款}\n other {一次性捐款}\n}已成功{interval, select,\n month {設定}\n year {設定}\n other {處理}\n}。\n\n捐款詳情:\n金額:{amount} {currency} {interval, select,\n month {每月}\n year {每年}\n other {}\n}\n\nStripe 稍後會另外寄給您一封收據,其中附有發票 PDF。這份收據包含所有付款詳情,可用於報稅。\n\n您可以隨時使用此連結查看您的捐款紀錄、下載發票,{interval, select,\n month {以及管理或取消您的訂閱}\n year {以及管理或取消您的訂閱}\n other {以及管理未來的捐款}\n}:\n\n{manageUrl}\n\n您的支持讓 {product_name} 得以持續運作。謝謝您!\n\n– {product_name} 團隊"
"body": "哈囉,\n\n感謝您對 {product_name} 的捐款!您的{interval, select,\n month {定期捐款}\n year {定期捐款}\n other {一次性捐款}\n}已成功{interval, select,\n month {設定}\n year {設定}\n other {處理}\n}。\n\n捐款詳情:\n金額:{amount} {interval, select,\n month {每月}\n year {每年}\n other {}\n}\n\nStripe 稍後會另外寄給您一封收據,其中附有發票 PDF。這份收據包含所有付款詳情,可用於報稅。\n\n您可以隨時使用此連結查看您的捐款紀錄、下載發票,{interval, select,\n month {以及管理或取消您的訂閱}\n year {以及管理或取消您的訂閱}\n other {以及管理未來的捐款}\n}:\n\n{manageUrl}\n\n您的支持讓 {product_name} 得以持續運作。謝謝您!\n\n– {product_name} 團隊"
},
"donation_magic_link": {
"subject": "管理您的 {product_name} 捐款",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "شكرًا لك على تبرعك لـ{product_name}",
"body": "مرحبًا،\n\nشكرًا لك على تبرعك لـ{product_name}! لقد تم {interval, select,\n month {إعداد تبرعك المتكرر}\n year {إعداد تبرعك المتكرر}\n other {معالجة تبرعك لمرة واحدة}\n} بنجاح.\n\nتفاصيل التبرع:\nالمبلغ: {amount} {currency} {interval, select,\n month {شهريًا}\n year {سنويًا}\n other {}\n}\n\nسترسل لك Stripe إيصالًا منفصلًا عبر البريد الإلكتروني مع فاتورتك بصيغة PDF قريبًا. يتضمن هذا جميع تفاصيل الدفع ويمكن استخدامه لأغراض ضريبية.\n\nيمكنك عرض سجل تبرعاتك، وتنزيل الفواتير، {interval, select,\n month {وإدارة أو إلغاء اشتراكك}\n year {وإدارة أو إلغاء اشتراكك}\n other {وإدارة التبرعات المستقبلية}\n} في أي وقت باستخدام هذا الرابط:\n\n{manageUrl}\n\nدعمك يساعد على استمرار {product_name}. شكرًا لك!\n\n– فريق {product_name}"
"body": "مرحبًا،\n\nشكرًا لك على تبرعك لـ{product_name}! لقد تم {interval, select,\n month {إعداد تبرعك المتكرر}\n year {إعداد تبرعك المتكرر}\n other {معالجة تبرعك لمرة واحدة}\n} بنجاح.\n\nتفاصيل التبرع:\nالمبلغ: {amount} {interval, select,\n month {شهريًا}\n year {سنويًا}\n other {}\n}\n\nسترسل لك Stripe إيصالًا منفصلًا عبر البريد الإلكتروني مع فاتورتك بصيغة PDF قريبًا. يتضمن هذا جميع تفاصيل الدفع ويمكن استخدامه لأغراض ضريبية.\n\nيمكنك عرض سجل تبرعاتك، وتنزيل الفواتير، {interval, select,\n month {وإدارة أو إلغاء اشتراكك}\n year {وإدارة أو إلغاء اشتراكك}\n other {وإدارة التبرعات المستقبلية}\n} في أي وقت باستخدام هذا الرابط:\n\n{manageUrl}\n\nدعمك يساعد على استمرار {product_name}. شكرًا لك!\n\n– فريق {product_name}"
},
"donation_magic_link": {
"subject": "إدارة تبرعاتك لـ{product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Благодарим ти за дарението към {product_name}",
"body": "Здравей,\n\nБлагодарим ти за дарението към {product_name}! Твоето {interval, select,\n month {периодично дарение}\n year {периодично дарение}\n other {еднократно дарение}\n} беше {interval, select,\n month {настроено}\n year {настроено}\n other {обработено}\n} успешно.\n\nПодробности за дарението:\nСума: {amount} {currency} {interval, select,\n month {на месец}\n year {на година}\n other {}\n}\n\nСкоро Stripe ще ти изпрати по имейл отделна разписка с PDF на фактурата ти. Тя включва всички данни за плащане и може да се използва за данъчни цели.\n\nМожеш да преглеждаш историята на даренията си, да изтегляш фактури {interval, select,\n month {и да управляваш или анулираш абонамента си}\n year {и да управляваш или анулираш абонамента си}\n other {и да управляваш бъдещи дарения}\n} по всяко време, като използваш този линк:\n\n{manageUrl}\n\nТвоята подкрепа помага на {product_name} да продължи да работи. Благодарим ти!\n\n– Екип на {product_name}"
"body": "Здравей,\n\nБлагодарим ти за дарението към {product_name}! Твоето {interval, select,\n month {периодично дарение}\n year {периодично дарение}\n other {еднократно дарение}\n} беше {interval, select,\n month {настроено}\n year {настроено}\n other {обработено}\n} успешно.\n\nПодробности за дарението:\nСума: {amount} {interval, select,\n month {на месец}\n year {на година}\n other {}\n}\n\nСкоро Stripe ще ти изпрати по имейл отделна разписка с PDF на фактурата ти. Тя включва всички данни за плащане и може да се използва за данъчни цели.\n\nМожеш да преглеждаш историята на даренията си, да изтегляш фактури {interval, select,\n month {и да управляваш или анулираш абонамента си}\n year {и да управляваш или анулираш абонамента си}\n other {и да управляваш бъдещи дарения}\n} по всяко време, като използваш този линк:\n\n{manageUrl}\n\nТвоята подкрепа помага на {product_name} да продължи да работи. Благодарим ти!\n\n– Екип на {product_name}"
},
"donation_magic_link": {
"subject": "Управлявай даренията си за {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Děkujeme za váš dar pro {product_name}",
"body": "Dobrý den,\n\nDěkujeme za váš dar pro {product_name}! Váš {interval, select,\n month {opakovaný dar}\n year {opakovaný dar}\n other {jednorázový dar}\n} byl úspěšně {interval, select,\n month {nastaven}\n year {nastaven}\n other {zpracován}\n}.\n\nPodrobnosti o daru:\nČástka: {amount} {currency} {interval, select,\n month {měsíčně}\n year {ročně}\n other {}\n}\n\nStripe vám brzy pošle samostatné potvrzení platby s fakturou v PDF. Obsahuje všechny platební údaje a lze ho použít pro daňové účely.\n\nPomocí tohoto odkazu si můžete kdykoli prohlédnout historii svých darů, stáhnout faktury {interval, select,\n month {a spravovat nebo zrušit předplatné}\n year {a spravovat nebo zrušit předplatné}\n other {a spravovat budoucí dary}\n}:\n\n{manageUrl}\n\nVaše podpora pomáhá udržovat {product_name} v chodu. Děkujeme!\n\n– Tým {product_name}"
"body": "Dobrý den,\n\nDěkujeme za váš dar pro {product_name}! Váš {interval, select,\n month {opakovaný dar}\n year {opakovaný dar}\n other {jednorázový dar}\n} byl úspěšně {interval, select,\n month {nastaven}\n year {nastaven}\n other {zpracován}\n}.\n\nPodrobnosti o daru:\nČástka: {amount} {interval, select,\n month {měsíčně}\n year {ročně}\n other {}\n}\n\nStripe vám brzy pošle samostatné potvrzení platby s fakturou v PDF. Obsahuje všechny platební údaje a lze ho použít pro daňové účely.\n\nPomocí tohoto odkazu si můžete kdykoli prohlédnout historii svých darů, stáhnout faktury {interval, select,\n month {a spravovat nebo zrušit předplatné}\n year {a spravovat nebo zrušit předplatné}\n other {a spravovat budoucí dary}\n}:\n\n{manageUrl}\n\nVaše podpora pomáhá udržovat {product_name} v chodu. Děkujeme!\n\n– Tým {product_name}"
},
"donation_magic_link": {
"subject": "Spravujte své dary pro {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Tak for din {product_name}-donation",
"body": "Hej,\n\nTak for din donation til {product_name}! Din {interval, select,\n month {løbende donation}\n year {løbende donation}\n other {engangsdonation}\n} er blevet {interval, select,\n month {oprettet}\n year {oprettet}\n other {gennemført}\n}.\n\nDonationsdetaljer:\nBeløb: {amount} {currency} {interval, select,\n month {pr. måned}\n year {pr. år}\n other {}\n}\n\nStripe sender dig en separat kvittering med din faktura som PDF inden længe. Kvitteringen indeholder alle betalingsdetaljer og kan bruges til skatteformål.\n\nDu kan til enhver tid se din donationshistorik, downloade fakturaer {interval, select,\n month {og administrere eller opsige dit abonnement}\n year {og administrere eller opsige dit abonnement}\n other {og administrere fremtidige donationer}\n} ved at bruge dette link:\n\n{manageUrl}\n\nDin støtte hjælper med at holde {product_name} kørende. Tak!\n\n– Teamet bag {product_name}"
"body": "Hej,\n\nTak for din donation til {product_name}! Din {interval, select,\n month {løbende donation}\n year {løbende donation}\n other {engangsdonation}\n} er blevet {interval, select,\n month {oprettet}\n year {oprettet}\n other {gennemført}\n}.\n\nDonationsdetaljer:\nBeløb: {amount} {interval, select,\n month {pr. måned}\n year {pr. år}\n other {}\n}\n\nStripe sender dig en separat kvittering med din faktura som PDF inden længe. Kvitteringen indeholder alle betalingsdetaljer og kan bruges til skatteformål.\n\nDu kan til enhver tid se din donationshistorik, downloade fakturaer {interval, select,\n month {og administrere eller opsige dit abonnement}\n year {og administrere eller opsige dit abonnement}\n other {og administrere fremtidige donationer}\n} ved at bruge dette link:\n\n{manageUrl}\n\nDin støtte hjælper med at holde {product_name} kørende. Tak!\n\n– Teamet bag {product_name}"
},
"donation_magic_link": {
"subject": "Administrer dine {product_name}-donationer",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Vielen Dank für deine {product_name}-Spende",
"body": "Hallo,\n\nvielen Dank für deine Spende an {product_name}! Deine {interval, select,\n month {wiederkehrende Spende}\n year {wiederkehrende Spende}\n other {einmalige Spende}\n} wurde erfolgreich {interval, select,\n month {eingerichtet}\n year {eingerichtet}\n other {verarbeitet}\n}.\n\nDetails zur Spende:\nBetrag: {amount} {currency} {interval, select,\n month {pro Monat}\n year {pro Jahr}\n other {}\n}\n\nStripe sendet dir in Kürze eine separate Quittung mit deiner Rechnungs-PDF per E-Mail. Diese enthält alle Zahlungsdetails und kann für Steuerzwecke verwendet werden.\n\nÜber diesen Link kannst du jederzeit deine Spendenübersicht aufrufen, Rechnungen herunterladen {interval, select,\n month {und dein Abonnement verwalten oder kündigen}\n year {und dein Abonnement verwalten oder kündigen}\n other {und zukünftige Spenden verwalten}\n}:\n\n{manageUrl}\n\nDeine Unterstützung hilft, {product_name} am Laufen zu halten. Vielen Dank!\n\n– {product_name}-Team"
"body": "Hallo,\n\nvielen Dank für deine Spende an {product_name}! Deine {interval, select,\n month {wiederkehrende Spende}\n year {wiederkehrende Spende}\n other {einmalige Spende}\n} wurde erfolgreich {interval, select,\n month {eingerichtet}\n year {eingerichtet}\n other {verarbeitet}\n}.\n\nDetails zur Spende:\nBetrag: {amount} {interval, select,\n month {pro Monat}\n year {pro Jahr}\n other {}\n}\n\nStripe sendet dir in Kürze eine separate Quittung mit deiner Rechnungs-PDF per E-Mail. Diese enthält alle Zahlungsdetails und kann für Steuerzwecke verwendet werden.\n\nÜber diesen Link kannst du jederzeit deine Spendenübersicht aufrufen, Rechnungen herunterladen {interval, select,\n month {und dein Abonnement verwalten oder kündigen}\n year {und dein Abonnement verwalten oder kündigen}\n other {und zukünftige Spenden verwalten}\n}:\n\n{manageUrl}\n\nDeine Unterstützung hilft, {product_name} am Laufen zu halten. Vielen Dank!\n\n– {product_name}-Team"
},
"donation_magic_link": {
"subject": "Verwalte deine {product_name}-Spenden",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Ευχαριστούμε για τη δωρεά σου στο {product_name}",
"body": "Γεια σου,\n\nΕυχαριστούμε για τη δωρεά σου στο {product_name}! Η {interval, select,\n month {επαναλαμβανόμενη δωρεά}\n year {επαναλαμβανόμενη δωρεά}\n other {εφάπαξ δωρεά}\n} σου έχει {interval, select,\n month {ρυθμιστεί}\n year {ρυθμιστεί}\n other {ολοκληρωθεί}\n} με επιτυχία.\n\nΛεπτομέρειες δωρεάς:\nΠοσό: {amount} {currency} {interval, select,\n month {ανά μήνα}\n year {ανά έτος}\n other {}\n}\n\nΗ Stripe θα σου στείλει σύντομα ξεχωριστή απόδειξη με το PDF του τιμολογίου σου. Περιλαμβάνει όλες τις λεπτομέρειες πληρωμής και μπορεί να χρησιμοποιηθεί για φορολογικούς σκοπούς.\n\nΜπορείς να δεις το ιστορικό των δωρεών σου, να κατεβάσεις τιμολόγια, {interval, select,\n month {και να διαχειριστείς ή να ακυρώσεις τη συνδρομή σου}\n year {και να διαχειριστείς ή να ακυρώσεις τη συνδρομή σου}\n other {και να διαχειριστείς μελλοντικές δωρεές}\n} ανά πάσα στιγμή χρησιμοποιώντας αυτόν τον σύνδεσμο:\n\n{manageUrl}\n\nΗ υποστήριξή σου βοηθάει το {product_name} να συνεχίσει να λειτουργεί. Ευχαριστούμε!\n\n– Η ομάδα του {product_name}"
"body": "Γεια σου,\n\nΕυχαριστούμε για τη δωρεά σου στο {product_name}! Η {interval, select,\n month {επαναλαμβανόμενη δωρεά}\n year {επαναλαμβανόμενη δωρεά}\n other {εφάπαξ δωρεά}\n} σου έχει {interval, select,\n month {ρυθμιστεί}\n year {ρυθμιστεί}\n other {ολοκληρωθεί}\n} με επιτυχία.\n\nΛεπτομέρειες δωρεάς:\nΠοσό: {amount} {interval, select,\n month {ανά μήνα}\n year {ανά έτος}\n other {}\n}\n\nΗ Stripe θα σου στείλει σύντομα ξεχωριστή απόδειξη με το PDF του τιμολογίου σου. Περιλαμβάνει όλες τις λεπτομέρειες πληρωμής και μπορεί να χρησιμοποιηθεί για φορολογικούς σκοπούς.\n\nΜπορείς να δεις το ιστορικό των δωρεών σου, να κατεβάσεις τιμολόγια, {interval, select,\n month {και να διαχειριστείς ή να ακυρώσεις τη συνδρομή σου}\n year {και να διαχειριστείς ή να ακυρώσεις τη συνδρομή σου}\n other {και να διαχειριστείς μελλοντικές δωρεές}\n} ανά πάσα στιγμή χρησιμοποιώντας αυτόν τον σύνδεσμο:\n\n{manageUrl}\n\nΗ υποστήριξή σου βοηθάει το {product_name} να συνεχίσει να λειτουργεί. Ευχαριστούμε!\n\n– Η ομάδα του {product_name}"
},
"donation_magic_link": {
"subject": "Διαχειρίσου τις δωρεές σου στο {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Thank you for your {product_name} donation",
"body": "Hello,\n\nThank you for your donation to {product_name}! Your {interval, select,\n month {recurring donation}\n year {recurring donation}\n other {one-time donation}\n} has been {interval, select,\n month {set up}\n year {set up}\n other {processed}\n} successfully.\n\nDonation details:\nAmount: {amount} {currency} {interval, select,\n month {per month}\n year {per year}\n other {}\n}\n\nStripe will email you a separate receipt with your invoice PDF shortly. This includes all payment details and can be used for tax purposes.\n\nYou can view your donation history, download invoices, {interval, select,\n month {and manage or cancel your subscription}\n year {and manage or cancel your subscription}\n other {and manage future donations}\n} at any time using this link:\n\n{manageUrl}\n\nYour support helps keep {product_name} running. Thank you!\n\n– {product_name} Team"
"body": "Hello,\n\nThank you for your donation to {product_name}! Your {interval, select,\n month {recurring donation}\n year {recurring donation}\n other {one-time donation}\n} has been {interval, select,\n month {set up}\n year {set up}\n other {processed}\n} successfully.\n\nDonation details:\nAmount: {amount} {interval, select,\n month {per month}\n year {per year}\n other {}\n}\n\nStripe will email you a separate receipt with your invoice PDF shortly. This includes all payment details and can be used for tax purposes.\n\nYou can view your donation history, download invoices, {interval, select,\n month {and manage or cancel your subscription}\n year {and manage or cancel your subscription}\n other {and manage future donations}\n} at any time using this link:\n\n{manageUrl}\n\nYour support helps keep {product_name} running. Thank you!\n\n– {product_name} Team"
},
"donation_magic_link": {
"subject": "Manage your {product_name} donations",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Gracias por tu donación a {product_name}",
"body": "Hola:\n\n¡Gracias por tu donación a {product_name}! Tu {interval, select,\n month {donación recurrente}\n year {donación recurrente}\n other {donación única}\n} ha sido {interval, select,\n month {configurada}\n year {configurada}\n other {procesada}\n} con éxito.\n\nDetalles de la donación:\nMonto: {amount} {currency} {interval, select,\n month {por mes}\n year {por año}\n other {}\n}\n\nEn breve, Stripe te enviará por separado un correo electrónico con el recibo y la factura en PDF. Este recibo incluye todos los detalles de pago y se puede utilizar para fines fiscales.\n\nPuedes ver tu historial de donaciones, descargar facturas {interval, select,\n month {y administrar o cancelar tu suscripción}\n year {y administrar o cancelar tu suscripción}\n other {y administrar futuras donaciones}\n} en cualquier momento usando este enlace:\n\n{manageUrl}\n\nTu apoyo ayuda a mantener {product_name} en funcionamiento. ¡Gracias!\n\n– Equipo de {product_name}"
"body": "Hola:\n\n¡Gracias por tu donación a {product_name}! Tu {interval, select,\n month {donación recurrente}\n year {donación recurrente}\n other {donación única}\n} ha sido {interval, select,\n month {configurada}\n year {configurada}\n other {procesada}\n} con éxito.\n\nDetalles de la donación:\nMonto: {amount} {interval, select,\n month {por mes}\n year {por año}\n other {}\n}\n\nEn breve, Stripe te enviará por separado un correo electrónico con el recibo y la factura en PDF. Este recibo incluye todos los detalles de pago y se puede utilizar para fines fiscales.\n\nPuedes ver tu historial de donaciones, descargar facturas {interval, select,\n month {y administrar o cancelar tu suscripción}\n year {y administrar o cancelar tu suscripción}\n other {y administrar futuras donaciones}\n} en cualquier momento usando este enlace:\n\n{manageUrl}\n\nTu apoyo ayuda a mantener {product_name} en funcionamiento. ¡Gracias!\n\n– Equipo de {product_name}"
},
"donation_magic_link": {
"subject": "Administra tus donaciones a {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Gracias por tu donación a {product_name}",
"body": "Hola:\n\n¡Gracias por tu donación a {product_name}! Tu {interval, select,\n month {donación recurrente}\n year {donación recurrente}\n other {donación única}\n} se ha {interval, select,\n month {configurado}\n year {configurado}\n other {procesado}\n} correctamente.\n\nDetalles de la donación:\nImporte: {amount} {currency} {interval, select,\n month {al mes}\n year {al año}\n other {}\n}\n\nEn breve, Stripe te enviará por separado un correo electrónico con el recibo y la factura en PDF. Este recibo incluye todos los detalles de pago y se puede utilizar para fines fiscales.\n\nPuedes ver tu historial de donaciones, descargar facturas {interval, select,\n month {y gestionar o cancelar tu suscripción}\n year {y gestionar o cancelar tu suscripción}\n other {y gestionar futuras donaciones}\n} en cualquier momento usando este enlace:\n\n{manageUrl}\n\nTu apoyo ayuda a mantener {product_name} en funcionamiento. ¡Gracias!\n\n– Equipo de {product_name}"
"body": "Hola:\n\n¡Gracias por tu donación a {product_name}! Tu {interval, select,\n month {donación recurrente}\n year {donación recurrente}\n other {donación única}\n} se ha {interval, select,\n month {configurado}\n year {configurado}\n other {procesado}\n} correctamente.\n\nDetalles de la donación:\nImporte: {amount} {interval, select,\n month {al mes}\n year {al año}\n other {}\n}\n\nEn breve, Stripe te enviará por separado un correo electrónico con el recibo y la factura en PDF. Este recibo incluye todos los detalles de pago y se puede utilizar para fines fiscales.\n\nPuedes ver tu historial de donaciones, descargar facturas {interval, select,\n month {y gestionar o cancelar tu suscripción}\n year {y gestionar o cancelar tu suscripción}\n other {y gestionar futuras donaciones}\n} en cualquier momento usando este enlace:\n\n{manageUrl}\n\nTu apoyo ayuda a mantener {product_name} en funcionamiento. ¡Gracias!\n\n– Equipo de {product_name}"
},
"donation_magic_link": {
"subject": "Gestiona tus donaciones a {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Kiitos {product_name}-lahjoituksestasi",
"body": "Hei,\n\nKiitos {product_name}-lahjoituksestasi! {interval, select,\n month {Toistuva lahjoituksesi}\n year {Toistuva lahjoituksesi}\n other {Kertalahjoituksesi}\n} on {interval, select,\n month {käynnistetty}\n year {käynnistetty}\n other {käsitelty}\n} onnistuneesti.\n\nLahjoituksen tiedot:\nSumma: {amount} {currency} {interval, select,\n month {kuukaudessa}\n year {vuodessa}\n other {}\n}\n\nStripe lähettää sinulle pian sähköpostitse erillisen kuitin sekä PDF-laskun. Niissä ovat kaikki maksutiedot, ja niitä voi käyttää verotuksessa.\n\nVoit tarkastella lahjoitushistoriaasi, ladata laskuja {interval, select,\n month {ja hallinnoida tai peruuttaa tilaustasi}\n year {ja hallinnoida tai peruuttaa tilaustasi}\n other {ja hallita tulevia lahjoituksia}\n} milloin tahansa käyttämällä tätä linkkiä:\n\n{manageUrl}\n\nTukesi auttaa pitämään {product_name}-palvelun toiminnassa. Kiitos!\n\n– {product_name}-tiimi"
"body": "Hei,\n\nKiitos {product_name}-lahjoituksestasi! {interval, select,\n month {Toistuva lahjoituksesi}\n year {Toistuva lahjoituksesi}\n other {Kertalahjoituksesi}\n} on {interval, select,\n month {käynnistetty}\n year {käynnistetty}\n other {käsitelty}\n} onnistuneesti.\n\nLahjoituksen tiedot:\nSumma: {amount} {interval, select,\n month {kuukaudessa}\n year {vuodessa}\n other {}\n}\n\nStripe lähettää sinulle pian sähköpostitse erillisen kuitin sekä PDF-laskun. Niissä ovat kaikki maksutiedot, ja niitä voi käyttää verotuksessa.\n\nVoit tarkastella lahjoitushistoriaasi, ladata laskuja {interval, select,\n month {ja hallinnoida tai peruuttaa tilaustasi}\n year {ja hallinnoida tai peruuttaa tilaustasi}\n other {ja hallita tulevia lahjoituksia}\n} milloin tahansa käyttämällä tätä linkkiä:\n\n{manageUrl}\n\nTukesi auttaa pitämään {product_name}-palvelun toiminnassa. Kiitos!\n\n– {product_name}-tiimi"
},
"donation_magic_link": {
"subject": "Hallitse {product_name}-lahjoituksiasi",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Merci pour votre don à {product_name}",
"body": "Bonjour,\n\nMerci pour votre don à {product_name} ! {interval, select, month {Votre don récurrent a bien été mis en place.} year {Votre don récurrent a bien été mis en place.} other {Votre don ponctuel a bien été traité.}}\n\nDétails du don :\nMontant : {amount} {currency} {interval, select, month {par mois} year {par an} other {}}\n\nStripe vous enverra bientôt un reçu séparé accompagné de votre facture PDF. Ce document contient tous les détails du paiement et peut servir de justificatif fiscal.\n\nVous pouvez consulter l’historique de vos dons, télécharger des factures {interval, select, month {et gérer ou résilier votre abonnement} year {et gérer ou résilier votre abonnement} other {et gérer vos futurs dons}} à tout moment avec ce lien :\n\n{manageUrl}\n\nVotre soutien contribue au fonctionnement de {product_name}. Merci !\n\n– L’équipe {product_name}"
"body": "Bonjour,\n\nMerci pour votre don à {product_name} ! {interval, select, month {Votre don récurrent a bien été mis en place.} year {Votre don récurrent a bien été mis en place.} other {Votre don ponctuel a bien été traité.}}\n\nDétails du don :\nMontant : {amount} {interval, select, month {par mois} year {par an} other {}}\n\nStripe vous enverra bientôt un reçu séparé accompagné de votre facture PDF. Ce document contient tous les détails du paiement et peut servir de justificatif fiscal.\n\nVous pouvez consulter l’historique de vos dons, télécharger des factures {interval, select, month {et gérer ou résilier votre abonnement} year {et gérer ou résilier votre abonnement} other {et gérer vos futurs dons}} à tout moment avec ce lien :\n\n{manageUrl}\n\nVotre soutien contribue au fonctionnement de {product_name}. Merci !\n\n– L’équipe {product_name}"
},
"donation_magic_link": {
"subject": "Gérer vos dons à {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "תודה על תרומתכם ל-{product_name}",
"body": "שלום,\n\nתודה על תרומתכם ל-{product_name}! ה{interval, select,\n month {תרומה החוזרת}\n year {תרומה החוזרת}\n other {תרומה החד-פעמית}\n} שלכם {interval, select,\n month {הוגדרה}\n year {הוגדרה}\n other {בוצעה}\n} בהצלחה.\n\nפרטי התרומה:\nסכום: {amount} {currency} {interval, select,\n month {לחודש}\n year {לשנה}\n other {}\n}\n\nStripe ישלח לכם בקרוב קבלה נפרדת עם קובץ PDF של החשבונית. הקבלה תכלול את כל פרטי התשלום וניתן יהיה להשתמש בה למטרות מס.\n\nאתם יכולים לצפות בהיסטוריית התרומות שלכם, להוריד חשבוניות, {interval, select,\n month {ולנהל או לבטל את המנוי}\n year {ולנהל או לבטל את המנוי}\n other {ולנהל תרומות עתידיות}\n} בכל עת באמצעות קישור זה:\n\n{manageUrl}\n\nהתמיכה שלכם עוזרת לשמור על פעילות {product_name}. תודה!\n\nצוות {product_name}"
"body": "שלום,\n\nתודה על תרומתכם ל-{product_name}! ה{interval, select,\n month {תרומה החוזרת}\n year {תרומה החוזרת}\n other {תרומה החד-פעמית}\n} שלכם {interval, select,\n month {הוגדרה}\n year {הוגדרה}\n other {בוצעה}\n} בהצלחה.\n\nפרטי התרומה:\nסכום: {amount} {interval, select,\n month {לחודש}\n year {לשנה}\n other {}\n}\n\nStripe ישלח לכם בקרוב קבלה נפרדת עם קובץ PDF של החשבונית. הקבלה תכלול את כל פרטי התשלום וניתן יהיה להשתמש בה למטרות מס.\n\nאתם יכולים לצפות בהיסטוריית התרומות שלכם, להוריד חשבוניות, {interval, select,\n month {ולנהל או לבטל את המנוי}\n year {ולנהל או לבטל את המנוי}\n other {ולנהל תרומות עתידיות}\n} בכל עת באמצעות קישור זה:\n\n{manageUrl}\n\nהתמיכה שלכם עוזרת לשמור על פעילות {product_name}. תודה!\n\nצוות {product_name}"
},
"donation_magic_link": {
"subject": "ניהול התרומות שלכם ל-{product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "आपके {product_name} डोनेशन के लिए धन्यवाद",
"body": "नमस्ते,\n\n{product_name} को आपके डोनेशन के लिए धन्यवाद! आपका {interval, select,\n month {रिकरिंग डोनेशन}\n year {रिकरिंग डोनेशन}\n other {वन-टाइम डोनेशन}\n} सफलतापूर्वक {interval, select,\n month {सेट अप हो गया है}\n year {सेट अप हो गया है}\n other {प्रोसेस हो गया है}\n}।\n\nडोनेशन का विवरण:\nराशि: {amount} {currency} {interval, select,\n month {प्रति माह}\n year {प्रति वर्ष}\n other {}\n}\n\nStripe आपको जल्द ही आपके इनवॉइस PDF के साथ एक अलग रसीद ईमेल करेगा। इसमें पेमेंट की सारी जानकारी शामिल है और इसे टैक्स के लिए इस्तेमाल किया जा सकता है।\n\nआप इस लिंक से किसी भी समय अपने डोनेशन का इतिहास देख सकते हैं, इनवॉइस डाउनलोड कर सकते हैं, {interval, select,\n month {और अपना सब्सक्रिप्शन मैनेज या कैंसल कर सकते हैं}\n year {और अपना सब्सक्रिप्शन मैनेज या कैंसल कर सकते हैं}\n other {और आगे के डोनेशन मैनेज कर सकते हैं}\n}।\n\n{manageUrl}\n\nआपका सपोर्ट {product_name} को चालू रखने में मदद करता है। धन्यवाद!\n\n– {product_name} टीम"
"body": "नमस्ते,\n\n{product_name} को आपके डोनेशन के लिए धन्यवाद! आपका {interval, select,\n month {रिकरिंग डोनेशन}\n year {रिकरिंग डोनेशन}\n other {वन-टाइम डोनेशन}\n} सफलतापूर्वक {interval, select,\n month {सेट अप हो गया है}\n year {सेट अप हो गया है}\n other {प्रोसेस हो गया है}\n}।\n\nडोनेशन का विवरण:\nराशि: {amount} {interval, select,\n month {प्रति माह}\n year {प्रति वर्ष}\n other {}\n}\n\nStripe आपको जल्द ही आपके इनवॉइस PDF के साथ एक अलग रसीद ईमेल करेगा। इसमें पेमेंट की सारी जानकारी शामिल है और इसे टैक्स के लिए इस्तेमाल किया जा सकता है।\n\nआप इस लिंक से किसी भी समय अपने डोनेशन का इतिहास देख सकते हैं, इनवॉइस डाउनलोड कर सकते हैं, {interval, select,\n month {और अपना सब्सक्रिप्शन मैनेज या कैंसल कर सकते हैं}\n year {और अपना सब्सक्रिप्शन मैनेज या कैंसल कर सकते हैं}\n other {और आगे के डोनेशन मैनेज कर सकते हैं}\n}।\n\n{manageUrl}\n\nआपका सपोर्ट {product_name} को चालू रखने में मदद करता है। धन्यवाद!\n\n– {product_name} टीम"
},
"donation_magic_link": {
"subject": "अपने {product_name} डोनेशन मैनेज करें",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Hvala ti na donaciji za {product_name}",
"body": "Pozdrav,\n\nHvala ti na donaciji za {product_name}! Tvoja {interval, select,\n month {redovita donacija}\n year {redovita donacija}\n other {jednokratna donacija}\n} uspješno je {interval, select,\n month {postavljena}\n year {postavljena}\n other {obrađena}\n}.\n\nDetalji donacije:\nIznos: {amount} {currency} {interval, select,\n month {mjesečno}\n year {godišnje}\n other {}\n}\n\nStripe će ti uskoro poslati zasebnu potvrdu s PDF-om računa. Ona sadrži sve detalje plaćanja i možeš je koristiti u porezne svrhe.\n\nMožeš pregledati povijest donacija, preuzeti račune {interval, select,\n month {i upravljati pretplatom ili je otkazati}\n year {i upravljati pretplatom ili je otkazati}\n other {i upravljati budućim donacijama}\n} u bilo kojem trenutku putem ove poveznice:\n\n{manageUrl}\n\nTvoja podrška pomaže da {product_name} nastavi raditi. Hvala ti!\n\n– Tim {product_name}a"
"body": "Pozdrav,\n\nHvala ti na donaciji za {product_name}! Tvoja {interval, select,\n month {redovita donacija}\n year {redovita donacija}\n other {jednokratna donacija}\n} uspješno je {interval, select,\n month {postavljena}\n year {postavljena}\n other {obrađena}\n}.\n\nDetalji donacije:\nIznos: {amount} {interval, select,\n month {mjesečno}\n year {godišnje}\n other {}\n}\n\nStripe će ti uskoro poslati zasebnu potvrdu s PDF-om računa. Ona sadrži sve detalje plaćanja i možeš je koristiti u porezne svrhe.\n\nMožeš pregledati povijest donacija, preuzeti račune {interval, select,\n month {i upravljati pretplatom ili je otkazati}\n year {i upravljati pretplatom ili je otkazati}\n other {i upravljati budućim donacijama}\n} u bilo kojem trenutku putem ove poveznice:\n\n{manageUrl}\n\nTvoja podrška pomaže da {product_name} nastavi raditi. Hvala ti!\n\n– Tim {product_name}a"
},
"donation_magic_link": {
"subject": "Upravljanje donacijama za {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Köszönjük, hogy adományoddal támogatod a {product_name} működését",
"body": "Szia!\n\nKöszönjük, hogy adományoddal támogatod a {product_name} működését! {interval, select,\n month {Sikeresen beállítottuk a rendszeres adományodat.}\n year {Sikeresen beállítottuk a rendszeres adományodat.}\n other {Sikeresen feldolgoztuk az egyszeri adományodat.}\n}\n\nAz adomány részletei:\nÖsszeg: {amount} {currency} {interval, select,\n month {havonta}\n year {évente}\n other {}\n}\n\nA Stripe hamarosan külön e-mailben küld nyugtát és egy PDF-számlát. Ezek tartalmazzák az összes fizetési adatot, és adózási célokra is használhatók.\n\nAz alábbi linken bármikor megtekintheted korábbi adományaidat, letöltheted a számlákat, {interval, select,\n month {valamint kezelheted vagy lemondhatod az előfizetésedet}\n year {valamint kezelheted vagy lemondhatod az előfizetésedet}\n other {valamint kezelheted a jövőbeli adományaidat}\n}:\n\n{manageUrl}\n\nTámogatásod segít fenntartani a {product_name} működését. Köszönjük!\n\n– {product_name} csapata"
"body": "Szia!\n\nKöszönjük, hogy adományoddal támogatod a {product_name} működését! {interval, select,\n month {Sikeresen beállítottuk a rendszeres adományodat.}\n year {Sikeresen beállítottuk a rendszeres adományodat.}\n other {Sikeresen feldolgoztuk az egyszeri adományodat.}\n}\n\nAz adomány részletei:\nÖsszeg: {amount} {interval, select,\n month {havonta}\n year {évente}\n other {}\n}\n\nA Stripe hamarosan külön e-mailben küld nyugtát és egy PDF-számlát. Ezek tartalmazzák az összes fizetési adatot, és adózási célokra is használhatók.\n\nAz alábbi linken bármikor megtekintheted korábbi adományaidat, letöltheted a számlákat, {interval, select,\n month {valamint kezelheted vagy lemondhatod az előfizetésedet}\n year {valamint kezelheted vagy lemondhatod az előfizetésedet}\n other {valamint kezelheted a jövőbeli adományaidat}\n}:\n\n{manageUrl}\n\nTámogatásod segít fenntartani a {product_name} működését. Köszönjük!\n\n– {product_name} csapata"
},
"donation_magic_link": {
"subject": "A {product_name} működését támogató adományaid kezelése",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Terima kasih atas donasimu untuk {product_name}",
"body": "Halo,\n\nTerima kasih atas donasimu untuk {product_name}! Donasi {interval, select,\n month {berulang}\n year {berulang}\n other {satu kali}\n} kamu telah berhasil {interval, select,\n month {diatur}\n year {diatur}\n other {diproses}\n}.\n\nDetail donasi:\nJumlah: {amount} {currency} {interval, select,\n month {per bulan}\n year {per tahun}\n other {}\n}\n\nStripe akan segera mengirimi kamu email tanda terima terpisah beserta PDF fakturmu. Ini mencakup semua detail pembayaran dan bisa dipakai untuk keperluan pajak.\n\nKamu bisa melihat riwayat donasimu, mengunduh faktur, {interval, select,\n month {dan mengelola atau membatalkan langgananmu}\n year {dan mengelola atau membatalkan langgananmu}\n other {dan mengelola donasi di masa mendatang}\n} kapan saja lewat tautan ini:\n\n{manageUrl}\n\nDukunganmu membantu {product_name} tetap berjalan. Terima kasih!\n\n– Tim {product_name}"
"body": "Halo,\n\nTerima kasih atas donasimu untuk {product_name}! Donasi {interval, select,\n month {berulang}\n year {berulang}\n other {satu kali}\n} kamu telah berhasil {interval, select,\n month {diatur}\n year {diatur}\n other {diproses}\n}.\n\nDetail donasi:\nJumlah: {amount} {interval, select,\n month {per bulan}\n year {per tahun}\n other {}\n}\n\nStripe akan segera mengirimi kamu email tanda terima terpisah beserta PDF fakturmu. Ini mencakup semua detail pembayaran dan bisa dipakai untuk keperluan pajak.\n\nKamu bisa melihat riwayat donasimu, mengunduh faktur, {interval, select,\n month {dan mengelola atau membatalkan langgananmu}\n year {dan mengelola atau membatalkan langgananmu}\n other {dan mengelola donasi di masa mendatang}\n} kapan saja lewat tautan ini:\n\n{manageUrl}\n\nDukunganmu membantu {product_name} tetap berjalan. Terima kasih!\n\n– Tim {product_name}"
},
"donation_magic_link": {
"subject": "Kelola donasimu untuk {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Grazie per la tua donazione a {product_name}",
"body": "Ciao,\n\nGrazie per la tua donazione a {product_name}! La tua {interval, select,\n month {donazione ricorrente}\n year {donazione ricorrente}\n other {donazione una tantum}\n} è stata {interval, select,\n month {attivata}\n year {attivata}\n other {elaborata}\n} con successo.\n\nDettagli della donazione:\nImporto: {amount} {currency} {interval, select,\n month {al mese}\n year {all'anno}\n other {}\n}\n\nStripe ti invierà a breve una ricevuta separata con la tua fattura in PDF. Questa include tutti i dettagli del pagamento e può essere utilizzata a fini fiscali.\n\nPuoi visualizzare la cronologia delle tue donazioni, scaricare le fatture {interval, select,\n month {e gestire o disdire il tuo abbonamento}\n year {e gestire o disdire il tuo abbonamento}\n other {e gestire le future donazioni}\n} in qualsiasi momento utilizzando questo link:\n\n{manageUrl}\n\nIl tuo supporto aiuta a mantenere {product_name} in funzione. Grazie!\n\n– Il team di {product_name}"
"body": "Ciao,\n\nGrazie per la tua donazione a {product_name}! La tua {interval, select,\n month {donazione ricorrente}\n year {donazione ricorrente}\n other {donazione una tantum}\n} è stata {interval, select,\n month {attivata}\n year {attivata}\n other {elaborata}\n} con successo.\n\nDettagli della donazione:\nImporto: {amount} {interval, select,\n month {al mese}\n year {all'anno}\n other {}\n}\n\nStripe ti invierà a breve una ricevuta separata con la tua fattura in PDF. Questa include tutti i dettagli del pagamento e può essere utilizzata a fini fiscali.\n\nPuoi visualizzare la cronologia delle tue donazioni, scaricare le fatture {interval, select,\n month {e gestire o disdire il tuo abbonamento}\n year {e gestire o disdire il tuo abbonamento}\n other {e gestire le future donazioni}\n} in qualsiasi momento utilizzando questo link:\n\n{manageUrl}\n\nIl tuo supporto aiuta a mantenere {product_name} in funzione. Grazie!\n\n– Il team di {product_name}"
},
"donation_magic_link": {
"subject": "Gestisci le tue donazioni a {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "{product_name}へのご寄付ありがとうございます",
"body": "こんにちは。\n\n{product_name}へのご寄付ありがとうございます。{interval, select,\n month {定期寄付}\n year {定期寄付}\n other {1回のみの寄付}\n}の{interval, select,\n month {設定}\n year {設定}\n other {処理}\n}が完了しました。\n\n寄付の詳細:\n金額: {amount} {currency}{interval, select,\n month {(毎月)}\n year {(毎年)}\n other {}\n}\n\nStripeから、請求書PDF付きの領収書が別途メールで送信されます。これにはすべての支払い詳細が含まれており、税務目的にご利用いただけます。\n\n以下のリンクから、いつでも寄付履歴の表示、請求書のダウンロード、{interval, select,\n month {およびサブスクリプションの管理またはキャンセル}\n year {およびサブスクリプションの管理またはキャンセル}\n other {および今後の寄付の管理}\n}が可能です。\n\n{manageUrl}\n\n皆様のご支援が{product_name}の運営を支えています。ありがとうございます。\n\n– {product_name}チーム"
"body": "こんにちは。\n\n{product_name}へのご寄付ありがとうございます。{interval, select,\n month {定期寄付}\n year {定期寄付}\n other {1回のみの寄付}\n}の{interval, select,\n month {設定}\n year {設定}\n other {処理}\n}が完了しました。\n\n寄付の詳細:\n金額: {amount}{interval, select,\n month {(毎月)}\n year {(毎年)}\n other {}\n}\n\nStripeから、請求書PDF付きの領収書が別途メールで送信されます。これにはすべての支払い詳細が含まれており、税務目的にご利用いただけます。\n\n以下のリンクから、いつでも寄付履歴の表示、請求書のダウンロード、{interval, select,\n month {およびサブスクリプションの管理またはキャンセル}\n year {およびサブスクリプションの管理またはキャンセル}\n other {および今後の寄付の管理}\n}が可能です。\n\n{manageUrl}\n\n皆様のご支援が{product_name}の運営を支えています。ありがとうございます。\n\n– {product_name}チーム"
},
"donation_magic_link": {
"subject": "{product_name}への寄付を管理",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "{product_name} 후원에 감사드려요",
"body": "안녕하세요,\n\n{product_name}에 후원해 주셔서 감사해요! 회원님의 {interval, select,\n month {정기 후원}\n year {정기 후원}\n other {일회성 후원}\n}이 성공적으로 {interval, select,\n month {설정되었어요}\n year {설정되었어요}\n other {처리되었어요}\n}.\n\n후원 상세 정보:\n금액: {amount} {currency} {interval, select,\n month {매월}\n year {매년}\n other {}\n}\n\nStripe에서 곧 인보이스 PDF가 포함된 별도의 영수증을 이메일로 보내드릴 예정이에요. 이 영수증에는 모든 결제 세부 정보가 포함되어 있으며, 세금 목적으로 활용할 수 있어요.\n\n다음 링크를 통해 언제든지 후원 내역을 확인하고, 인보이스를 다운로드하고, {interval, select,\n month {구독을 관리하거나 취소할 수 있어요}\n year {구독을 관리하거나 취소할 수 있어요}\n other {향후 후원을 관리할 수 있어요}\n}:\n\n{manageUrl}\n\n회원님의 후원은 {product_name}를 계속 운영하는 데 큰 도움이 돼요. 진심으로 감사해요!\n\n– {product_name} 팀"
"body": "안녕하세요,\n\n{product_name}에 후원해 주셔서 감사해요! 회원님의 {interval, select,\n month {정기 후원}\n year {정기 후원}\n other {일회성 후원}\n}이 성공적으로 {interval, select,\n month {설정되었어요}\n year {설정되었어요}\n other {처리되었어요}\n}.\n\n후원 상세 정보:\n금액: {amount} {interval, select,\n month {매월}\n year {매년}\n other {}\n}\n\nStripe에서 곧 인보이스 PDF가 포함된 별도의 영수증을 이메일로 보내드릴 예정이에요. 이 영수증에는 모든 결제 세부 정보가 포함되어 있으며, 세금 목적으로 활용할 수 있어요.\n\n다음 링크를 통해 언제든지 후원 내역을 확인하고, 인보이스를 다운로드하고, {interval, select,\n month {구독을 관리하거나 취소할 수 있어요}\n year {구독을 관리하거나 취소할 수 있어요}\n other {향후 후원을 관리할 수 있어요}\n}:\n\n{manageUrl}\n\n회원님의 후원은 {product_name}를 계속 운영하는 데 큰 도움이 돼요. 진심으로 감사해요!\n\n– {product_name} 팀"
},
"donation_magic_link": {
"subject": "{product_name} 후원 내역 관리",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Dėkojame už tavo {product_name} paramą",
"body": "Sveiki,\n\nDėkojame už tavo paramą {product_name}! Tavo {interval, select,\n month {pasikartojanti parama}\n year {pasikartojanti parama}\n other {vienkartinė parama}\n} sėkmingai {interval, select,\n month {nustatyta}\n year {nustatyta}\n other {apdorota}\n}.\n\nParamos informacija:\nSuma: {amount} {currency} {interval, select,\n month {per mėnesį}\n year {per metus}\n other {}\n}\n\nStripe netrukus atsiųs tau atskirą kvitą su sąskaitos faktūros PDF. Jame nurodyta visa mokėjimo informacija ir jį galima naudoti mokesčių tikslais.\n\nBet kuriuo metu šia nuoroda gali peržiūrėti savo paramos istoriją, atsisiųsti sąskaitas faktūras {interval, select,\n month {ir valdyti arba atšaukti savo prenumeratą}\n year {ir valdyti arba atšaukti savo prenumeratą}\n other {ir valdyti būsimą paramą}\n}:\n\n{manageUrl}\n\nTavo parama padeda {product_name} veikti. Ačiū!\n\n– {product_name} komanda"
"body": "Sveiki,\n\nDėkojame už tavo paramą {product_name}! Tavo {interval, select,\n month {pasikartojanti parama}\n year {pasikartojanti parama}\n other {vienkartinė parama}\n} sėkmingai {interval, select,\n month {nustatyta}\n year {nustatyta}\n other {apdorota}\n}.\n\nParamos informacija:\nSuma: {amount} {interval, select,\n month {per mėnesį}\n year {per metus}\n other {}\n}\n\nStripe netrukus atsiųs tau atskirą kvitą su sąskaitos faktūros PDF. Jame nurodyta visa mokėjimo informacija ir jį galima naudoti mokesčių tikslais.\n\nBet kuriuo metu šia nuoroda gali peržiūrėti savo paramos istoriją, atsisiųsti sąskaitas faktūras {interval, select,\n month {ir valdyti arba atšaukti savo prenumeratą}\n year {ir valdyti arba atšaukti savo prenumeratą}\n other {ir valdyti būsimą paramą}\n}:\n\n{manageUrl}\n\nTavo parama padeda {product_name} veikti. Ačiū!\n\n– {product_name} komanda"
},
"donation_magic_link": {
"subject": "Tvarkyk savo {product_name} paramą",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Bedankt voor je {product_name}-donatie",
"body": "Hallo,\n\nBedankt voor je donatie aan {product_name}! Je {interval, select,\n month {terugkerende donatie}\n year {terugkerende donatie}\n other {eenmalige donatie}\n} is succesvol {interval, select,\n month {ingesteld}\n year {ingesteld}\n other {verwerkt}\n}.\n\nDonatiegegevens:\nBedrag: {amount} {currency} {interval, select,\n month {per maand}\n year {per jaar}\n other {}\n}\n\nStripe stuurt je binnenkort per e-mail een apart betalingsbewijs met je factuur als pdf. Dit bevat alle betalingsgegevens en kan worden gebruikt voor belastingdoeleinden.\n\nJe kunt op elk moment je donatiegeschiedenis bekijken, facturen downloaden en {interval, select,\n month {je abonnement beheren of opzeggen}\n year {je abonnement beheren of opzeggen}\n other {toekomstige donaties beheren}\n} via deze link:\n\n{manageUrl}\n\nJe steun helpt {product_name} draaiende te houden. Bedankt!\n\n– Het team van {product_name}"
"body": "Hallo,\n\nBedankt voor je donatie aan {product_name}! Je {interval, select,\n month {terugkerende donatie}\n year {terugkerende donatie}\n other {eenmalige donatie}\n} is succesvol {interval, select,\n month {ingesteld}\n year {ingesteld}\n other {verwerkt}\n}.\n\nDonatiegegevens:\nBedrag: {amount} {interval, select,\n month {per maand}\n year {per jaar}\n other {}\n}\n\nStripe stuurt je binnenkort per e-mail een apart betalingsbewijs met je factuur als pdf. Dit bevat alle betalingsgegevens en kan worden gebruikt voor belastingdoeleinden.\n\nJe kunt op elk moment je donatiegeschiedenis bekijken, facturen downloaden en {interval, select,\n month {je abonnement beheren of opzeggen}\n year {je abonnement beheren of opzeggen}\n other {toekomstige donaties beheren}\n} via deze link:\n\n{manageUrl}\n\nJe steun helpt {product_name} draaiende te houden. Bedankt!\n\n– Het team van {product_name}"
},
"donation_magic_link": {
"subject": "Beheer je {product_name}-donaties",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Takk for bidraget ditt til {product_name}",
"body": "Hei,\n\nTakk for bidraget ditt til {product_name}! {interval, select,\n month {Det faste bidraget ditt er opprettet.}\n year {Det faste bidraget ditt er opprettet.}\n other {Engangsbidraget ditt er behandlet.}\n}\n\nDetaljer om bidraget:\nBeløp: {amount} {currency} {interval, select,\n month {per måned}\n year {per år}\n other {}\n}\n\nStripe sender deg snart en egen kvittering med fakturaen som PDF. Kvitteringen inneholder alle betalingsdetaljer og kan brukes til skatteformål.\n\nDu kan når som helst se bidragshistorikken din, laste ned fakturaer og {interval, select,\n month {administrere eller si opp abonnementet ditt}\n year {administrere eller si opp abonnementet ditt}\n other {administrere framtidige bidrag}\n} via denne lenken:\n\n{manageUrl}\n\nStøtten din bidrar til å holde {product_name} i gang. Takk!\n\n– {product_name}-teamet"
"body": "Hei,\n\nTakk for bidraget ditt til {product_name}! {interval, select,\n month {Det faste bidraget ditt er opprettet.}\n year {Det faste bidraget ditt er opprettet.}\n other {Engangsbidraget ditt er behandlet.}\n}\n\nDetaljer om bidraget:\nBeløp: {amount} {interval, select,\n month {per måned}\n year {per år}\n other {}\n}\n\nStripe sender deg snart en egen kvittering med fakturaen som PDF. Kvitteringen inneholder alle betalingsdetaljer og kan brukes til skatteformål.\n\nDu kan når som helst se bidragshistorikken din, laste ned fakturaer og {interval, select,\n month {administrere eller si opp abonnementet ditt}\n year {administrere eller si opp abonnementet ditt}\n other {administrere framtidige bidrag}\n} via denne lenken:\n\n{manageUrl}\n\nStøtten din bidrar til å holde {product_name} i gang. Takk!\n\n– {product_name}-teamet"
},
"donation_magic_link": {
"subject": "Administrer {product_name}-bidragene dine",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Dziękujemy za darowiznę dla {product_name}",
"body": "Witaj,\n\nDziękujemy za darowiznę dla {product_name}! Twoja {interval, select,\n month {cykliczna darowizna}\n year {cykliczna darowizna}\n other {jednorazowa darowizna}\n} została {interval, select,\n month {uruchomiona}\n year {uruchomiona}\n other {przetworzona}\n} pomyślnie.\n\nSzczegóły darowizny:\nKwota: {amount} {currency} {interval, select,\n month {miesięcznie}\n year {rocznie}\n other {}\n}\n\nStripe wkrótce wyśle Ci osobną wiadomość e-mail z potwierdzeniem i fakturą w formacie PDF. Potwierdzenie zawiera wszystkie szczegóły płatności i może służyć do celów podatkowych.\n\nMożesz przeglądać historię swoich darowizn, pobierać faktury {interval, select,\n month {oraz zarządzać subskrypcją lub ją anulować}\n year {oraz zarządzać subskrypcją lub ją anulować}\n other {oraz zarządzać przyszłymi darowiznami}\n} w dowolnym momencie, korzystając z tego linku:\n\n{manageUrl}\n\nTwoje wsparcie pomaga utrzymać {product_name}. Dziękujemy!\n\n– Zespół {product_name}"
"body": "Witaj,\n\nDziękujemy za darowiznę dla {product_name}! Twoja {interval, select,\n month {cykliczna darowizna}\n year {cykliczna darowizna}\n other {jednorazowa darowizna}\n} została {interval, select,\n month {uruchomiona}\n year {uruchomiona}\n other {przetworzona}\n} pomyślnie.\n\nSzczegóły darowizny:\nKwota: {amount} {interval, select,\n month {miesięcznie}\n year {rocznie}\n other {}\n}\n\nStripe wkrótce wyśle Ci osobną wiadomość e-mail z potwierdzeniem i fakturą w formacie PDF. Potwierdzenie zawiera wszystkie szczegóły płatności i może służyć do celów podatkowych.\n\nMożesz przeglądać historię swoich darowizn, pobierać faktury {interval, select,\n month {oraz zarządzać subskrypcją lub ją anulować}\n year {oraz zarządzać subskrypcją lub ją anulować}\n other {oraz zarządzać przyszłymi darowiznami}\n} w dowolnym momencie, korzystając z tego linku:\n\n{manageUrl}\n\nTwoje wsparcie pomaga utrzymać {product_name}. Dziękujemy!\n\n– Zespół {product_name}"
},
"donation_magic_link": {
"subject": "Zarządzaj darowiznami dla {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Agradecemos pela sua doação ao {product_name}",
"body": "Olá,\n\nAgradecemos pela sua doação ao {product_name}! Sua {interval, select,\n month {doação recorrente}\n year {doação recorrente}\n other {doação única}\n} foi {interval, select,\n month {configurada}\n year {configurada}\n other {processada}\n} com sucesso.\n\nDetalhes da doação:\nValor: {amount} {currency} {interval, select,\n month {por mês}\n year {por ano}\n other {}\n}\n\nA Stripe enviará um recibo separado com sua fatura em PDF em breve. Isso inclui todos os detalhes de pagamento e pode ser usado para fins fiscais.\n\nVocê pode visualizar seu histórico de doações, baixar faturas {interval, select,\n month {e gerenciar ou cancelar sua assinatura}\n year {e gerenciar ou cancelar sua assinatura}\n other {e gerenciar futuras doações}\n} a qualquer momento usando este link:\n\n{manageUrl}\n\nSeu apoio ajuda a manter o {product_name} funcionando. Agradecemos!\n\n– Equipe do {product_name}"
"body": "Olá,\n\nAgradecemos pela sua doação ao {product_name}! Sua {interval, select,\n month {doação recorrente}\n year {doação recorrente}\n other {doação única}\n} foi {interval, select,\n month {configurada}\n year {configurada}\n other {processada}\n} com sucesso.\n\nDetalhes da doação:\nValor: {amount} {interval, select,\n month {por mês}\n year {por ano}\n other {}\n}\n\nA Stripe enviará um recibo separado com sua fatura em PDF em breve. Isso inclui todos os detalhes de pagamento e pode ser usado para fins fiscais.\n\nVocê pode visualizar seu histórico de doações, baixar faturas {interval, select,\n month {e gerenciar ou cancelar sua assinatura}\n year {e gerenciar ou cancelar sua assinatura}\n other {e gerenciar futuras doações}\n} a qualquer momento usando este link:\n\n{manageUrl}\n\nSeu apoio ajuda a manter o {product_name} funcionando. Agradecemos!\n\n– Equipe do {product_name}"
},
"donation_magic_link": {
"subject": "Gerencie suas doações ao {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Îți mulțumim pentru donația către {product_name}",
"body": "Salut,\n\nÎți mulțumim pentru donația ta către {product_name}! {interval, select,\n month {Donația ta recurentă}\n year {Donația ta recurentă}\n other {Donația ta unică}\n} a fost {interval, select,\n month {configurată}\n year {configurată}\n other {procesată}\n} cu succes.\n\nDetalii donație:\nSumă: {amount} {currency} {interval, select,\n month {pe lună}\n year {pe an}\n other {}\n}\n\nStripe îți va trimite în scurt timp un e-mail separat cu chitanța și factura ta în format PDF. Acestea includ toate detaliile plății și pot fi folosite în scopuri fiscale.\n\nPoți vizualiza istoricul donațiilor, descărca facturi {interval, select,\n month {și gestiona sau anula abonamentul}\n year {și gestiona sau anula abonamentul}\n other {și gestiona donațiile viitoare}\n} în orice moment folosind acest link:\n\n{manageUrl}\n\nSprijinul tău ne ajută să menținem {product_name} în funcțiune. Mulțumim!\n\n– Echipa {product_name}"
"body": "Salut,\n\nÎți mulțumim pentru donația ta către {product_name}! {interval, select,\n month {Donația ta recurentă}\n year {Donația ta recurentă}\n other {Donația ta unică}\n} a fost {interval, select,\n month {configurată}\n year {configurată}\n other {procesată}\n} cu succes.\n\nDetalii donație:\nSumă: {amount} {interval, select,\n month {pe lună}\n year {pe an}\n other {}\n}\n\nStripe îți va trimite în scurt timp un e-mail separat cu chitanța și factura ta în format PDF. Acestea includ toate detaliile plății și pot fi folosite în scopuri fiscale.\n\nPoți vizualiza istoricul donațiilor, descărca facturi {interval, select,\n month {și gestiona sau anula abonamentul}\n year {și gestiona sau anula abonamentul}\n other {și gestiona donațiile viitoare}\n} în orice moment folosind acest link:\n\n{manageUrl}\n\nSprijinul tău ne ajută să menținem {product_name} în funcțiune. Mulțumim!\n\n– Echipa {product_name}"
},
"donation_magic_link": {
"subject": "Gestionează-ți donațiile către {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Спасибо за твоё пожертвование для {product_name}",
"body": "Привет,\n\nСпасибо за твоё пожертвование для {product_name}! Твоё {interval, select,\n month {регулярное пожертвование}\n year {регулярное пожертвование}\n other {разовое пожертвование}\n} успешно {interval, select,\n month {настроено}\n year {настроено}\n other {обработано}\n}.\n\nДетали пожертвования:\nСумма: {amount} {currency} {interval, select,\n month {в месяц}\n year {в год}\n other {}\n}\n\nStripe вскоре отправит тебе отдельную квитанцию со счётом в формате PDF. В ней есть все платёжные данные, и её можно использовать для налоговых целей.\n\nТы можешь просматривать историю своих пожертвований, скачивать счета, {interval, select,\n month {а также управлять подпиской или отменить её}\n year {а также управлять подпиской или отменить её}\n other {и управлять будущими пожертвованиями}\n} в любое время по этой ссылке:\n\n{manageUrl}\n\nТвоя поддержка помогает {product_name} работать. Спасибо!\n\n– Команда {product_name}"
"body": "Привет,\n\nСпасибо за твоё пожертвование для {product_name}! Твоё {interval, select,\n month {регулярное пожертвование}\n year {регулярное пожертвование}\n other {разовое пожертвование}\n} успешно {interval, select,\n month {настроено}\n year {настроено}\n other {обработано}\n}.\n\nДетали пожертвования:\nСумма: {amount} {interval, select,\n month {в месяц}\n year {в год}\n other {}\n}\n\nStripe вскоре отправит тебе отдельную квитанцию со счётом в формате PDF. В ней есть все платёжные данные, и её можно использовать для налоговых целей.\n\nТы можешь просматривать историю своих пожертвований, скачивать счета, {interval, select,\n month {а также управлять подпиской или отменить её}\n year {а также управлять подпиской или отменить её}\n other {и управлять будущими пожертвованиями}\n} в любое время по этой ссылке:\n\n{manageUrl}\n\nТвоя поддержка помогает {product_name} работать. Спасибо!\n\n– Команда {product_name}"
},
"donation_magic_link": {
"subject": "Управляй своими пожертвованиями для {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Tack för din donation till {product_name}",
"body": "Hej,\n\nTack för din donation till {product_name}! {interval, select,\n month {Din återkommande donation har registrerats.}\n year {Din återkommande donation har registrerats.}\n other {Din engångsdonation har genomförts.}\n}\n\nDonationsuppgifter:\nBelopp: {amount} {currency} {interval, select,\n month {per månad}\n year {per år}\n other {}\n}\n\nStripe skickar snart ett separat kvitto med din faktura i PDF-format via e-post. Där finns alla betalningsuppgifter, och underlaget kan användas för skatteändamål.\n\nDu kan se din donationshistorik, ladda ner fakturor {interval, select,\n month {och hantera eller avsluta din återkommande donation}\n year {och hantera eller avsluta din återkommande donation}\n other {och hantera framtida donationer}\n} när som helst via den här länken:\n\n{manageUrl}\n\nDitt stöd hjälper till att hålla {product_name} igång. Tack!\n\n– {product_name}-teamet"
"body": "Hej,\n\nTack för din donation till {product_name}! {interval, select,\n month {Din återkommande donation har registrerats.}\n year {Din återkommande donation har registrerats.}\n other {Din engångsdonation har genomförts.}\n}\n\nDonationsuppgifter:\nBelopp: {amount} {interval, select,\n month {per månad}\n year {per år}\n other {}\n}\n\nStripe skickar snart ett separat kvitto med din faktura i PDF-format via e-post. Där finns alla betalningsuppgifter, och underlaget kan användas för skatteändamål.\n\nDu kan se din donationshistorik, ladda ner fakturor {interval, select,\n month {och hantera eller avsluta din återkommande donation}\n year {och hantera eller avsluta din återkommande donation}\n other {och hantera framtida donationer}\n} när som helst via den här länken:\n\n{manageUrl}\n\nDitt stöd hjälper till att hålla {product_name} igång. Tack!\n\n– {product_name}-teamet"
},
"donation_magic_link": {
"subject": "Hantera dina donationer till {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "ขอบคุณสำหรับการบริจาคให้ {product_name}",
"body": "สวัสดี,\n\nขอบคุณสำหรับการบริจาคให้ {product_name}! {interval, select,\n month {การบริจาคแบบต่อเนื่อง}\n year {การบริจาคแบบต่อเนื่อง}\n other {การบริจาคครั้งเดียว}\n}ของคุณได้รับการ{interval, select,\n month {ตั้งค่า}\n year {ตั้งค่า}\n other {ดำเนินการ}\n}เรียบร้อยแล้ว\n\nรายละเอียดการบริจาค:\nจำนวนเงิน: {amount} {currency} {interval, select,\n month {ต่อเดือน}\n year {ต่อปี}\n other {}\n}\n\nStripe จะส่งอีเมลใบเสร็จแยกต่างหากพร้อมไฟล์ PDF ใบแจ้งหนี้ให้คุณในไม่ช้า ใบเสร็จนี้มีรายละเอียดการชำระเงินทั้งหมดและใช้สำหรับวัตถุประสงค์ทางภาษีได้\n\nคุณสามารถดูประวัติการบริจาค ดาวน์โหลดใบแจ้งหนี้ {interval, select,\n month {และจัดการหรือยกเลิกการสมัครสมาชิกของคุณ}\n year {และจัดการหรือยกเลิกการสมัครสมาชิกของคุณ}\n other {และจัดการการบริจาคในอนาคต}\n} ได้ตลอดเวลาโดยใช้ลิงก์นี้:\n\n{manageUrl}\n\nการสนับสนุนของคุณช่วยให้ {product_name} ดำเนินต่อไปได้ ขอบคุณ!\n\n– ทีม {product_name}"
"body": "สวัสดี,\n\nขอบคุณสำหรับการบริจาคให้ {product_name}! {interval, select,\n month {การบริจาคแบบต่อเนื่อง}\n year {การบริจาคแบบต่อเนื่อง}\n other {การบริจาคครั้งเดียว}\n}ของคุณได้รับการ{interval, select,\n month {ตั้งค่า}\n year {ตั้งค่า}\n other {ดำเนินการ}\n}เรียบร้อยแล้ว\n\nรายละเอียดการบริจาค:\nจำนวนเงิน: {amount} {interval, select,\n month {ต่อเดือน}\n year {ต่อปี}\n other {}\n}\n\nStripe จะส่งอีเมลใบเสร็จแยกต่างหากพร้อมไฟล์ PDF ใบแจ้งหนี้ให้คุณในไม่ช้า ใบเสร็จนี้มีรายละเอียดการชำระเงินทั้งหมดและใช้สำหรับวัตถุประสงค์ทางภาษีได้\n\nคุณสามารถดูประวัติการบริจาค ดาวน์โหลดใบแจ้งหนี้ {interval, select,\n month {และจัดการหรือยกเลิกการสมัครสมาชิกของคุณ}\n year {และจัดการหรือยกเลิกการสมัครสมาชิกของคุณ}\n other {และจัดการการบริจาคในอนาคต}\n} ได้ตลอดเวลาโดยใช้ลิงก์นี้:\n\n{manageUrl}\n\nการสนับสนุนของคุณช่วยให้ {product_name} ดำเนินต่อไปได้ ขอบคุณ!\n\n– ทีม {product_name}"
},
"donation_magic_link": {
"subject": "จัดการการบริจาคให้ {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "{product_name}'a yaptığın bağış için teşekkürler",
"body": "Merhaba,\n\n{product_name}'a yaptığın bağış için teşekkürler! {interval, select,\n month {tekrarlayan bağışın}\n year {tekrarlayan bağışın}\n other {tek seferlik bağışın}\n} başarıyla {interval, select,\n month {ayarlandı}\n year {ayarlandı}\n other {işlendi}\n}.\n\nBağış detayları:\nTutar: {amount} {currency} {interval, select,\n month {aylık}\n year {yıllık}\n other {}\n}\n\nStripe, kısa süre içinde fatura PDF'ini içeren ayrı bir makbuzu sana e-posta ile gönderecektir. Bu, tüm ödeme detaylarını içerir ve vergi amaçlı kullanılabilir.\n\nBu bağlantıyı kullanarak istediğin zaman bağış geçmişini görüntüleyebilir, faturaları indirebilir, {interval, select,\n month {ve aboneliğini yönetebilir veya iptal edebilirsin}\n year {ve aboneliğini yönetebilir veya iptal edebilirsin}\n other {ve gelecekteki bağışlarını yönetebilirsin}\n}:\n\n{manageUrl}\n\nDesteğin {product_name}'ın çalışmaya devam etmesine yardımcı oluyor. Teşekkürler!\n\n– {product_name} Ekibi"
"body": "Merhaba,\n\n{product_name}'a yaptığın bağış için teşekkürler! {interval, select,\n month {tekrarlayan bağışın}\n year {tekrarlayan bağışın}\n other {tek seferlik bağışın}\n} başarıyla {interval, select,\n month {ayarlandı}\n year {ayarlandı}\n other {işlendi}\n}.\n\nBağış detayları:\nTutar: {amount} {interval, select,\n month {aylık}\n year {yıllık}\n other {}\n}\n\nStripe, kısa süre içinde fatura PDF'ini içeren ayrı bir makbuzu sana e-posta ile gönderecektir. Bu, tüm ödeme detaylarını içerir ve vergi amaçlı kullanılabilir.\n\nBu bağlantıyı kullanarak istediğin zaman bağış geçmişini görüntüleyebilir, faturaları indirebilir, {interval, select,\n month {ve aboneliğini yönetebilir veya iptal edebilirsin}\n year {ve aboneliğini yönetebilir veya iptal edebilirsin}\n other {ve gelecekteki bağışlarını yönetebilirsin}\n}:\n\n{manageUrl}\n\nDesteğin {product_name}'ın çalışmaya devam etmesine yardımcı oluyor. Teşekkürler!\n\n– {product_name} Ekibi"
},
"donation_magic_link": {
"subject": "{product_name} bağışlarını yönet",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Дякуємо за твою пожертву для {product_name}",
"body": "Привіт,\n\nДякуємо за твою пожертву для {product_name}! Твою {interval, select,\n month {регулярну пожертву}\n year {регулярну пожертву}\n other {одноразову пожертву}\n} успішно {interval, select,\n month {налаштовано}\n year {налаштовано}\n other {оброблено}\n}.\n\nДеталі пожертви:\nСума: {amount} {currency} {interval, select,\n month {на місяць}\n year {на рік}\n other {}\n}\n\nStripe незабаром надішле тобі окрему квитанцію з рахунком у форматі PDF. Вона містить усі платіжні дані, і її можна використати для податкових цілей.\n\nТи можеш переглядати історію своїх пожертв, завантажувати рахунки, {interval, select,\n month {а також керувати підпискою або скасувати її}\n year {а також керувати підпискою або скасувати її}\n other {а також керувати майбутніми пожертвами}\n} у будь-який час за цим посиланням:\n\n{manageUrl}\n\nТвоя підтримка допомагає {product_name} працювати. Дякуємо!\n\n– Команда {product_name}"
"body": "Привіт,\n\nДякуємо за твою пожертву для {product_name}! Твою {interval, select,\n month {регулярну пожертву}\n year {регулярну пожертву}\n other {одноразову пожертву}\n} успішно {interval, select,\n month {налаштовано}\n year {налаштовано}\n other {оброблено}\n}.\n\nДеталі пожертви:\nСума: {amount} {interval, select,\n month {на місяць}\n year {на рік}\n other {}\n}\n\nStripe незабаром надішле тобі окрему квитанцію з рахунком у форматі PDF. Вона містить усі платіжні дані, і її можна використати для податкових цілей.\n\nТи можеш переглядати історію своїх пожертв, завантажувати рахунки, {interval, select,\n month {а також керувати підпискою або скасувати її}\n year {а також керувати підпискою або скасувати її}\n other {а також керувати майбутніми пожертвами}\n} у будь-який час за цим посиланням:\n\n{manageUrl}\n\nТвоя підтримка допомагає {product_name} працювати. Дякуємо!\n\n– Команда {product_name}"
},
"donation_magic_link": {
"subject": "Керуй своїми пожертвами для {product_name}",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Cảm ơn bạn đã ủng hộ {product_name}",
"body": "Xin chào,\n\nCảm ơn bạn đã ủng hộ {product_name}! {interval, select,\n month {Khoản ủng hộ định kỳ}\n year {Khoản ủng hộ định kỳ}\n other {Khoản ủng hộ một lần}\n} của bạn đã được {interval, select,\n month {thiết lập}\n year {thiết lập}\n other {xử lý}\n} thành công.\n\nChi tiết ủng hộ:\nSố tiền: {amount} {currency} {interval, select,\n month {mỗi tháng}\n year {mỗi năm}\n other {}\n}\n\nStripe sẽ sớm gửi cho bạn một email biên lai riêng kèm hóa đơn PDF. Biên lai này gồm đầy đủ chi tiết thanh toán và có thể dùng cho mục đích thuế.\n\nBạn có thể xem lịch sử ủng hộ, tải xuống hóa đơn, {interval, select,\n month {và quản lý hoặc hủy gói đăng ký}\n year {và quản lý hoặc hủy gói đăng ký}\n other {và quản lý các khoản ủng hộ trong tương lai}\n} bất cứ lúc nào bằng cách sử dụng liên kết này:\n\n{manageUrl}\n\nSự hỗ trợ của bạn giúp {product_name} tiếp tục hoạt động. Cảm ơn bạn!\n\n– Đội ngũ {product_name}"
"body": "Xin chào,\n\nCảm ơn bạn đã ủng hộ {product_name}! {interval, select,\n month {Khoản ủng hộ định kỳ}\n year {Khoản ủng hộ định kỳ}\n other {Khoản ủng hộ một lần}\n} của bạn đã được {interval, select,\n month {thiết lập}\n year {thiết lập}\n other {xử lý}\n} thành công.\n\nChi tiết ủng hộ:\nSố tiền: {amount} {interval, select,\n month {mỗi tháng}\n year {mỗi năm}\n other {}\n}\n\nStripe sẽ sớm gửi cho bạn một email biên lai riêng kèm hóa đơn PDF. Biên lai này gồm đầy đủ chi tiết thanh toán và có thể dùng cho mục đích thuế.\n\nBạn có thể xem lịch sử ủng hộ, tải xuống hóa đơn, {interval, select,\n month {và quản lý hoặc hủy gói đăng ký}\n year {và quản lý hoặc hủy gói đăng ký}\n other {và quản lý các khoản ủng hộ trong tương lai}\n} bất cứ lúc nào bằng cách sử dụng liên kết này:\n\n{manageUrl}\n\nSự hỗ trợ của bạn giúp {product_name} tiếp tục hoạt động. Cảm ơn bạn!\n\n– Đội ngũ {product_name}"
},
"donation_magic_link": {
"subject": "Quản lý các khoản ủng hộ {product_name} của bạn",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "感谢你的 {product_name} 捐赠",
"body": "你好,\n\n感谢你对 {product_name} 的捐赠!你的{interval, select,\n month {定期捐赠}\n year {定期捐赠}\n other {一次性捐赠}\n}{interval, select,\n month {已成功设置}\n year {已成功设置}\n other {已成功处理}\n}。\n\n捐赠详情:\n金额:{amount} {currency} {interval, select,\n month {每月}\n year {每年}\n other {}\n}\n\nStripe 稍后会另外发送一封收据邮件,其中附有发票 PDF。邮件包含所有付款详情,可用于报税。\n\n你可以随时使用此链接查看你的捐赠历史、下载发票,以及{interval, select,\n month {管理或取消你的订阅}\n year {管理或取消你的订阅}\n other {管理你的未来捐赠}\n}:\n\n{manageUrl}\n\n你的支持有助于 {product_name} 的持续运行。谢谢!\n\n– {product_name} 团队"
"body": "你好,\n\n感谢你对 {product_name} 的捐赠!你的{interval, select,\n month {定期捐赠}\n year {定期捐赠}\n other {一次性捐赠}\n}{interval, select,\n month {已成功设置}\n year {已成功设置}\n other {已成功处理}\n}。\n\n捐赠详情:\n金额:{amount} {interval, select,\n month {每月}\n year {每年}\n other {}\n}\n\nStripe 稍后会另外发送一封收据邮件,其中附有发票 PDF。邮件包含所有付款详情,可用于报税。\n\n你可以随时使用此链接查看你的捐赠历史、下载发票,以及{interval, select,\n month {管理或取消你的订阅}\n year {管理或取消你的订阅}\n other {管理你的未来捐赠}\n}:\n\n{manageUrl}\n\n你的支持有助于 {product_name} 的持续运行。谢谢!\n\n– {product_name} 团队"
},
"donation_magic_link": {
"subject": "管理你的 {product_name} 捐赠",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "感謝您對 {product_name} 的捐款",
"body": "哈囉,\n\n感謝您對 {product_name} 的捐款!您的{interval, select,\n month {定期捐款}\n year {定期捐款}\n other {一次性捐款}\n}已成功{interval, select,\n month {設定}\n year {設定}\n other {處理}\n}。\n\n捐款詳情:\n金額:{amount} {currency} {interval, select,\n month {每月}\n year {每年}\n other {}\n}\n\nStripe 稍後會另外寄給您一封收據,其中附有發票 PDF。這份收據包含所有付款詳情,可用於報稅。\n\n您可以隨時使用此連結查看您的捐款紀錄、下載發票,{interval, select,\n month {以及管理或取消您的訂閱}\n year {以及管理或取消您的訂閱}\n other {以及管理未來的捐款}\n}:\n\n{manageUrl}\n\n您的支持讓 {product_name} 得以持續運作。謝謝您!\n\n– {product_name} 團隊"
"body": "哈囉,\n\n感謝您對 {product_name} 的捐款!您的{interval, select,\n month {定期捐款}\n year {定期捐款}\n other {一次性捐款}\n}已成功{interval, select,\n month {設定}\n year {設定}\n other {處理}\n}。\n\n捐款詳情:\n金額:{amount} {interval, select,\n month {每月}\n year {每年}\n other {}\n}\n\nStripe 稍後會另外寄給您一封收據,其中附有發票 PDF。這份收據包含所有付款詳情,可用於報稅。\n\n您可以隨時使用此連結查看您的捐款紀錄、下載發票,{interval, select,\n month {以及管理或取消您的訂閱}\n year {以及管理或取消您的訂閱}\n other {以及管理未來的捐款}\n}:\n\n{manageUrl}\n\n您的支持讓 {product_name} 得以持續運作。謝謝您!\n\n– {product_name} 團隊"
},
"donation_magic_link": {
"subject": "管理您的 {product_name} 捐款",
@@ -13,7 +13,7 @@
},
"donation_confirmation": {
"subject": "Thank you for your {product_name} donation",
"body": "Hello,\n\nThank you for your donation to {product_name}! Your {interval, select,\n month {recurring donation}\n year {recurring donation}\n other {one-time donation}\n} has been {interval, select,\n month {set up}\n year {set up}\n other {processed}\n} successfully.\n\nDonation details:\nAmount: {amount} {currency} {interval, select,\n month {per month}\n year {per year}\n other {}\n}\n\nStripe will email you a separate receipt with your invoice PDF shortly. This includes all payment details and can be used for tax purposes.\n\nYou can view your donation history, download invoices, {interval, select,\n month {and manage or cancel your subscription}\n year {and manage or cancel your subscription}\n other {and manage future donations}\n} at any time using this link:\n\n{manageUrl}\n\nYour support helps keep {product_name} running. Thank you!\n\n– {product_name} Team"
"body": "Hello,\n\nThank you for your donation to {product_name}! Your {interval, select,\n month {recurring donation}\n year {recurring donation}\n other {one-time donation}\n} has been {interval, select,\n month {set up}\n year {set up}\n other {processed}\n} successfully.\n\nDonation details:\nAmount: {amount} {interval, select,\n month {per month}\n year {per year}\n other {}\n}\n\nStripe will email you a separate receipt with your invoice PDF shortly. This includes all payment details and can be used for tax purposes.\n\nYou can view your donation history, download invoices, {interval, select,\n month {and manage or cancel your subscription}\n year {and manage or cancel your subscription}\n other {and manage future donations}\n} at any time using this link:\n\n{manageUrl}\n\nYour support helps keep {product_name} running. Thank you!\n\n– {product_name} Team"
},
"donation_magic_link": {
"subject": "Manage your {product_name} donations",
+1 -2
View File
@@ -285,6 +285,5 @@ export function formatGeoipLocation(result: GeoipResult, locale?: string | null)
const localizedCountry = locale && result.countryCode ? countryDisplayName(result.countryCode, locale) : null;
const countryLabel = localizedCountry ?? result.countryName ?? result.countryCode;
if (countryLabel) parts.push(countryLabel);
if (parts.length === 0) return null;
return new Intl.ListFormat(locale ?? 'en', {style: 'narrow', type: 'unit'}).format(parts);
return parts.length > 0 ? parts.join(', ') : null;
}
+1 -1
View File
@@ -29,7 +29,7 @@ export interface IKVSubscription {
}
export interface KVPurgeBatchResult {
urls: Array<string>;
entries: Array<string>;
tokensConsumed: number;
}
+11 -11
View File
@@ -217,7 +217,7 @@ local refillIntervalMs = tonumber(ARGV[5])
local queueSize = redis.call('SCARD', queueKey)
if queueSize == 0 then
return '{"urls":[],"tokens":0}'
return '{"entries":[],"tokens":0}'
end
local tokens = maxTokens
@@ -237,14 +237,14 @@ end
local toPop = math.min(maxItems, math.floor(tokens), queueSize)
if toPop <= 0 then
redis.call('SET', bucketKey, cjson.encode({tokens = tokens, lastRefill = lastRefill}), 'EX', 3600)
return '{"urls":[],"tokens":0}'
return '{"entries":[],"tokens":0}'
end
local urls = redis.call('SPOP', queueKey, toPop)
tokens = tokens - #urls
local entries = redis.call('SPOP', queueKey, toPop)
tokens = tokens - #entries
redis.call('SET', bucketKey, cjson.encode({tokens = tokens, lastRefill = lastRefill}), 'EX', 3600)
return cjson.encode({urls = urls, tokens = #urls})
return cjson.encode({entries = entries, tokens = #entries})
`;
const CLAIM_BULK_DELETION_SCRIPT = `
local score = redis.call('ZSCORE', KEYS[1], ARGV[1])
@@ -902,17 +902,17 @@ function parseRateLimitResult(value: unknown): KVRateLimitResult {
function parsePurgeBatchResult(value: unknown, maxItems: number): KVPurgeBatchResult {
const command = 'dequeuePurgeBatch';
if (!isJsonObject(value)) throw createInvalidResponseError(command, 'a purge batch object');
const {urls, tokens} = value;
const {entries, tokens} = value;
if (
!Array.isArray(urls) ||
!urls.every((url): url is string => typeof url === 'string') ||
!Array.isArray(entries) ||
!entries.every((entry): entry is string => typeof entry === 'string') ||
!isNonNegativeSafeInteger(tokens) ||
tokens !== urls.length ||
urls.length > maxItems
tokens !== entries.length ||
entries.length > maxItems
) {
throw createInvalidResponseError(command, 'a bounded string array and matching token count');
}
return {urls, tokensConsumed: tokens};
return {entries, tokensConsumed: tokens};
}
function isJsonObject(value: unknown): value is Record<string, unknown> {
@@ -153,7 +153,7 @@ describe('KVClient script execution', () => {
},
{
name: 'dequeuePurgeBatch',
reply: JSON.stringify({urls: ['https://fluxer.test/a.png'], tokens: 1}),
reply: JSON.stringify({entries: ['/attachments/1/2/a'], tokens: 1}),
keyCount: 2,
run: async (client) => client.dequeuePurgeBatch('queue:key', 'bucket:key', 10, 10, 1, 1000),
},
+12
View File
@@ -147,6 +147,10 @@ export function buildAPIConfigFromMaster(master: MasterConfig): APIConfig {
if (Buffer.from(uploadRelaySecretBase64, 'base64').length < 32) {
throw new Error('FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64 must decode to at least 32 bytes');
}
const donationProxyKey = (master.services.api.donation_proxy_key ?? '').trim();
if (donationProxyKey.length > 0 && donationProxyKey.length < 32) {
throw new Error('FLUXER_API_DONATION_PROXY_KEY must be at least 32 characters');
}
if (!s3Config) {
throw new Error('S3 configuration is required for the API');
}
@@ -233,6 +237,11 @@ export function buildAPIConfigFromMaster(master: MasterConfig): APIConfig {
jetStreamUrl: master.services.nats?.jetstream_url ?? 'nats://127.0.0.1:4223',
authToken: master.services.nats?.auth_token ?? '',
},
storageChangeFeed: {
enabled: master.services.api.storage_change_feed?.enabled ?? false,
stream: master.services.api.storage_change_feed?.stream ?? 'STORAGE_CHANGES',
skipBuckets: master.services.api.storage_change_feed?.skip_buckets ?? [s3Buckets.uploads],
},
search: {
engine: master.integrations.search?.engine ?? 'elasticsearch',
url: master.integrations.search?.url ?? 'http://127.0.0.1:9200',
@@ -275,6 +284,7 @@ export function buildAPIConfigFromMaster(master: MasterConfig): APIConfig {
internal: {
gateway: resolveGatewayInternalUrl(master),
gatewayRpcAuthToken: master.services.gateway.rpc_auth_token ?? '',
donationProxyKey,
},
hosts: {
invite: extractHostname(master.endpoints.invite),
@@ -469,6 +479,8 @@ export function buildAPIConfigFromMaster(master: MasterConfig): APIConfig {
wordmarkUrl: master.instance.branding.wordmark_url,
faviconUrl: master.instance.branding.favicon_url,
themeColor: master.instance.branding.theme_color,
statusPageUrl: master.instance.branding.status_page_url,
statusPageIncidentHistoryUrl: master.instance.branding.status_page_incident_history_url,
},
setup: {
configured: master.instance.setup.configured,
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import type {ValueOf} from '@fluxer/constants/src/ValueOf';
import type {AdminAuditAccess} from '@fluxer/schema/src/domains/admin/AdminSchemas';
export const AdminAuditReadActions = {
CHECK_BLOCKLIST_ENTRY: 'check_blocklist_entry',
GET_ADMIN_API_KEY: 'get_admin_api_key',
GET_APPLICATION: 'get_application',
GET_ARCHIVE: 'get_archive',
GET_ARCHIVE_DOWNLOAD_URL: 'get_archive_download_url',
GET_AUDIT_LOG: 'get_audit_log',
GET_GATEWAY_STATS: 'get_gateway_stats',
GET_GUILD: 'get_guild',
GET_INSTANCE_CONFIG: 'get_instance_config',
GET_LIMIT_CONFIG: 'get_limit_config',
GET_MESSAGE: 'get_message',
GET_MESSAGE_SHRED_STATUS: 'get_message_shred_status',
GET_REPORT: 'get_report',
GET_USER: 'get_user',
GET_VOICE_REGION: 'get_voice_region',
GET_VOICE_SERVER: 'get_voice_server',
GET_VOICE_STATE_COUNTS: 'get_voice_state_counts',
LIST_ADMIN_ACLS: 'list_admin_acls',
LIST_ADMIN_API_KEYS: 'list_admin_api_keys',
LIST_ARCHIVES: 'list_archives',
LIST_AUDIT_LOGS: 'list_audit_logs',
LIST_BLOCKLIST_ENTRIES: 'list_blocklist_entries',
LIST_BLOCKLISTS: 'list_blocklists',
LIST_CHANNEL_MESSAGES: 'list_channel_messages',
LIST_DISCOVERY_APPLICATIONS: 'list_discovery_applications',
LIST_DISCOVERY_CATEGORIES: 'list_discovery_categories',
LIST_DISCOVERY_CATEGORY_LISTINGS: 'list_discovery_category_listings',
LIST_DISCOVERY_LISTINGS: 'list_discovery_listings',
LIST_GUILD_APPLICATIONS: 'list_guild_applications',
LIST_GUILD_AUDIT_LOGS: 'list_guild_audit_logs',
LIST_GUILD_EMOJIS: 'list_guild_emojis',
LIST_GUILD_MEMBERS: 'list_guild_members',
LIST_GUILD_MEMORY_STATS: 'list_guild_memory_stats',
LIST_GUILD_STICKERS: 'list_guild_stickers',
LIST_USER_APPLICATIONS: 'list_user_applications',
LIST_USER_CHANGE_LOG: 'list_user_change_log',
LIST_USER_DM_CHANNELS: 'list_user_dm_channels',
LIST_USER_GUILDS: 'list_user_guilds',
LIST_USER_RELATIONSHIPS: 'list_user_relationships',
LIST_USER_SESSIONS: 'list_user_sessions',
LIST_VOICE_REGIONS: 'list_voice_regions',
LIST_VOICE_SERVERS: 'list_voice_servers',
LIST_WEBAUTHN_CREDENTIALS: 'list_webauthn_credentials',
SEARCH_AUDIT_LOGS: 'search_audit_logs',
SEARCH_GUILDS: 'search_guilds',
SEARCH_MESSAGES: 'search_messages',
SEARCH_REPORTS: 'search_reports',
SEARCH_USERS: 'search_users',
} as const;
export type AdminAuditReadAction = ValueOf<typeof AdminAuditReadActions>;
export const ADMIN_AUDIT_READ_ACTIONS: ReadonlyArray<AdminAuditReadAction> = Object.values(AdminAuditReadActions);
const READ_ACTION_SET: ReadonlySet<string> = new Set(ADMIN_AUDIT_READ_ACTIONS);
export function getAdminAuditAccess(action: string): AdminAuditAccess {
return READ_ACTION_SET.has(action) ? 'read' : 'write';
}
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import type {AdminAuditReadAction} from '@app/api/admin/AdminAuditActions';
import type {HonoEnv} from '@app/api/types/HonoEnv';
import type {Context} from 'hono';
type AdminAuditMetadataValue = string | number | bigint | boolean | null | undefined;
type AdminAuditMetadataInput = Readonly<Record<string, AdminAuditMetadataValue>>;
interface AdminAuditEntryInput<TAction extends string> {
targetType: string;
targetId: bigint;
action: TAction;
metadata?: AdminAuditMetadataInput;
}
const SNOWFLAKE_PATTERN = /^(0|[1-9][0-9]{0,19})$/;
export function snowflakeOrUndefined(value: string | undefined): string | undefined {
return value !== undefined && SNOWFLAKE_PATTERN.test(value) ? value : undefined;
}
function toAdminAuditMetadata(input: AdminAuditMetadataInput = {}): Map<string, string> {
const metadata = new Map<string, string>();
for (const [key, value] of Object.entries(input)) {
if (value === undefined || value === null) continue;
metadata.set(key, String(value));
}
return metadata;
}
async function recordAdminAuditEntry(ctx: Context<HonoEnv>, entry: AdminAuditEntryInput<string>): Promise<void> {
await ctx.get('adminService').auditService.createAuditLog({
adminUserId: ctx.get('adminUserId'),
targetType: entry.targetType,
targetId: entry.targetId,
action: entry.action,
auditLogReason: ctx.get('auditLogReason'),
metadata: toAdminAuditMetadata(entry.metadata),
});
}
export async function recordAdminRead(
ctx: Context<HonoEnv>,
entry: AdminAuditEntryInput<AdminAuditReadAction>,
): Promise<void> {
await recordAdminAuditEntry(ctx, entry);
}
export async function recordAdminWrite(ctx: Context<HonoEnv>, entry: AdminAuditEntryInput<string>): Promise<void> {
await recordAdminAuditEntry(ctx, entry);
}
@@ -1,5 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {AdminAuditReadActions} from '@app/api/admin/AdminAuditActions';
import {recordAdminRead, recordAdminWrite} from '@app/api/admin/AdminAuditRecorder';
import type {AdminApiKeyView} from '@app/api/admin/services/AdminApiKeyService';
import {requireAdminACL} from '@app/api/middleware/AdminMiddleware';
import {RateLimitMiddleware} from '@app/api/middleware/RateLimitMiddleware';
@@ -62,6 +64,15 @@ export function AdminApiKeyAdminController(app: HonoApp) {
expires_at: result.apiKey.expiresAt?.toISOString() ?? null,
acls: Array.from(result.apiKey.acls),
};
await recordAdminWrite(ctx, {
targetType: 'admin_api_key',
targetId: BigInt(result.apiKey.keyId),
action: 'create_admin_api_key',
metadata: {
acls: response.acls.join(','),
expires_in_days: request.expires_in_days,
},
});
return ctx.json(response);
},
);
@@ -84,6 +95,12 @@ export function AdminApiKeyAdminController(app: HonoApp) {
const user = ctx.get('user');
const keys = await adminApiKeyService.listKeys(user.id);
const response: Array<ListAdminApiKeyResponseType> = keys.map(toApiKeyResponse);
await recordAdminRead(ctx, {
targetType: 'admin_api_key',
targetId: 0n,
action: AdminAuditReadActions.LIST_ADMIN_API_KEYS,
metadata: {result_count: response.length},
});
return ctx.json(response);
},
);
@@ -107,6 +124,11 @@ export function AdminApiKeyAdminController(app: HonoApp) {
const user = ctx.get('user');
const keyId = ctx.req.valid('param').key_id;
const key = await adminApiKeyService.getKey(keyId, user.id);
await recordAdminRead(ctx, {
targetType: 'admin_api_key',
targetId: keyId,
action: AdminAuditReadActions.GET_ADMIN_API_KEY,
});
return ctx.json(toApiKeyResponse(key));
},
);
@@ -131,8 +153,19 @@ export function AdminApiKeyAdminController(app: HonoApp) {
const user = ctx.get('user');
const adminUserAcls = ctx.get('adminUserAcls');
const keyId = ctx.req.valid('param').key_id;
const key = await adminApiKeyService.updateKey(keyId, user.id, ctx.req.valid('json'), adminUserAcls);
return ctx.json(toApiKeyResponse(key));
const request = ctx.req.valid('json');
const key = await adminApiKeyService.updateKey(keyId, user.id, request, adminUserAcls);
const response = toApiKeyResponse(key);
await recordAdminWrite(ctx, {
targetType: 'admin_api_key',
targetId: keyId,
action: 'update_admin_api_key',
metadata: {
fields: (['name', 'acls'] as const).filter((field) => request[field] !== undefined).join(','),
acls: request.acls !== undefined ? response.acls.join(',') : undefined,
},
});
return ctx.json(response);
},
);
app.delete(
@@ -155,6 +188,11 @@ export function AdminApiKeyAdminController(app: HonoApp) {
const user = ctx.get('user');
const keyId = ctx.req.valid('param').key_id;
await adminApiKeyService.revokeKey(keyId, user.id);
await recordAdminWrite(ctx, {
targetType: 'admin_api_key',
targetId: keyId,
action: 'revoke_admin_api_key',
});
return ctx.json({success: true}, 200);
},
);

Some files were not shown because too many files have changed in this diff Show More