feat: add i18n persistence model

This commit is contained in:
poyrazavsever
2026-07-19 03:06:47 +03:00
parent f4a2342ac4
commit 805beccf95
11 changed files with 4670 additions and 3 deletions
+2
View File
@@ -125,6 +125,7 @@ export const portalInvitations = sqliteTable(
tokenHash: text("token_hash").notNull(),
clientId: text("client_id").notNull(),
email: text("email").notNull(),
locale: text("locale").default("tr").notNull(),
status: text("status").$type<PortalInvitationStatus>()
.default("pending")
.notNull(),
@@ -137,6 +138,7 @@ export const portalInvitations = sqliteTable(
uniqueIndex("portal_invitations_token_hash_unique").on(table.tokenHash),
index("portal_invitations_client_id_idx").on(table.clientId),
index("portal_invitations_email_idx").on(table.email),
index("portal_invitations_locale_idx").on(table.locale),
],
);
+2
View File
@@ -45,6 +45,7 @@ export const clients = sqliteTable(
email: text("email"),
phone: text("phone"),
website: text("website"),
portalLocale: text("portal_locale"),
status: text("status").$type<ClientStatus>().default("active").notNull(),
pipelineStage: text("pipeline_stage").$type<ClientPipelineStage>().default("lead").notNull(),
nextFollowUpDate: text("next_follow_up_date"),
@@ -60,6 +61,7 @@ export const clients = sqliteTable(
index("clients_owner_user_id_idx").on(table.ownerUserId),
index("clients_owner_status_idx").on(table.ownerUserId, table.status),
index("clients_owner_pipeline_idx").on(table.ownerUserId, table.pipelineStage),
index("clients_portal_locale_idx").on(table.portalLocale),
index("clients_next_follow_up_date_idx").on(table.nextFollowUpDate),
check("clients_status_check", sql`${table.status} in ('active', 'paused', 'archived')`),
check(
+122
View File
@@ -0,0 +1,122 @@
import { sql } from "drizzle-orm";
import {
check,
index,
integer,
sqliteTable,
text,
uniqueIndex,
} from "drizzle-orm/sqlite-core";
export type LocaleStatus = "draft" | "active" | "archived" | "test";
export type TextDirection = "ltr" | "rtl";
export type TranslationEntityType =
| "branding"
| "calendar_event"
| "client"
| "planning_section"
| "project"
| "task";
const nowMs = sql`(cast(unixepoch('subsecond') * 1000 as integer))`;
export const instanceLocales = sqliteTable(
"instance_locales",
{
code: text("code").primaryKey(),
name: text("name").notNull(),
nativeName: text("native_name").notNull(),
status: text("status").$type<LocaleStatus>().default("draft").notNull(),
fallbackLocale: text("fallback_locale"),
textDirection: text("text_direction").$type<TextDirection>().default("ltr").notNull(),
builtIn: integer("built_in", { mode: "boolean" }).default(false).notNull(),
sortOrder: integer("sort_order").default(0).notNull(),
createdAt: integer("created_at", { mode: "timestamp_ms" }).default(nowMs).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp_ms" })
.default(nowMs)
.$onUpdate(() => new Date())
.notNull(),
},
(table) => [
index("instance_locales_status_idx").on(table.status),
index("instance_locales_fallback_idx").on(table.fallbackLocale),
check(
"instance_locales_code_check",
sql`${table.code} glob '[a-z][a-z]' or ${table.code} glob '[a-z][a-z]-[A-Z][A-Z]' or ${table.code} glob '[a-z][a-z]-[A-Z][A-Z][0-9]'`,
),
check("instance_locales_status_check", sql`${table.status} in ('draft', 'active', 'archived', 'test')`),
check("instance_locales_direction_check", sql`${table.textDirection} in ('ltr', 'rtl')`),
check("instance_locales_not_self_fallback_check", sql`${table.fallbackLocale} is null or ${table.fallbackLocale} != ${table.code}`),
],
);
export const instanceI18nSettings = sqliteTable("instance_i18n_settings", {
key: text("key").primaryKey().default("default").notNull(),
defaultLocale: text("default_locale").default("tr").notNull(),
catalogVersion: integer("catalog_version").default(1).notNull(),
createdAt: integer("created_at", { mode: "timestamp_ms" }).default(nowMs).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp_ms" })
.default(nowMs)
.$onUpdate(() => new Date())
.notNull(),
});
export const instanceUiTranslations = sqliteTable(
"instance_ui_translations",
{
id: integer("id").primaryKey({ autoIncrement: true }),
locale: text("locale").notNull(),
namespace: text("namespace").notNull(),
translationKey: text("translation_key").notNull(),
value: text("value").notNull(),
createdAt: integer("created_at", { mode: "timestamp_ms" }).default(nowMs).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp_ms" })
.default(nowMs)
.$onUpdate(() => new Date())
.notNull(),
},
(table) => [
uniqueIndex("instance_ui_translations_locale_key_unique").on(
table.locale,
table.namespace,
table.translationKey,
),
index("instance_ui_translations_locale_namespace_idx").on(table.locale, table.namespace),
check("instance_ui_translations_locale_check", sql`length(${table.locale}) between 2 and 12`),
check("instance_ui_translations_namespace_check", sql`length(${table.namespace}) between 1 and 64`),
check("instance_ui_translations_key_check", sql`length(${table.translationKey}) between 1 and 160`),
],
);
export const contentTranslations = sqliteTable(
"content_translations",
{
id: integer("id").primaryKey({ autoIncrement: true }),
entityType: text("entity_type").$type<TranslationEntityType>().notNull(),
entityId: text("entity_id").notNull(),
field: text("field").notNull(),
locale: text("locale").notNull(),
value: text("value").notNull(),
createdAt: integer("created_at", { mode: "timestamp_ms" }).default(nowMs).notNull(),
updatedAt: integer("updated_at", { mode: "timestamp_ms" })
.default(nowMs)
.$onUpdate(() => new Date())
.notNull(),
},
(table) => [
uniqueIndex("content_translations_entity_field_locale_unique").on(
table.entityType,
table.entityId,
table.field,
table.locale,
),
index("content_translations_locale_idx").on(table.locale),
index("content_translations_entity_locale_idx").on(table.entityType, table.entityId, table.locale),
check(
"content_translations_entity_type_check",
sql`${table.entityType} in ('branding', 'calendar_event', 'client', 'planning_section', 'project', 'task')`,
),
check("content_translations_field_check", sql`length(${table.field}) between 1 and 64`),
check("content_translations_locale_check", sql`length(${table.locale}) between 2 and 12`),
],
);
+1
View File
@@ -1,5 +1,6 @@
export * from "./auth";
export * from "./domain";
export * from "./i18n";
export * from "./runtime";
export * from "./settings";
export * from "./storage";
+1 -1
View File
@@ -48,7 +48,7 @@ export const userPreferences = sqliteTable(
},
(table) => [
check("user_preferences_currency_check", sql`length(${table.defaultCurrency}) = 3`),
check("user_preferences_language_check", sql`${table.language} in ('tr', 'en')`),
check("user_preferences_language_check", sql`length(${table.language}) between 2 and 12`),
check("user_preferences_color_mode_check", sql`${table.colorMode} in ('light', 'dark', 'system')`),
],
);