@endora-commerce/mod-languages 0.100.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +50 -0
- package/dist/backend/entities/language.entity.d.ts +23 -0
- package/dist/backend/entities/language.entity.d.ts.map +1 -0
- package/dist/backend/entities/language.entity.js +78 -0
- package/dist/backend/entities/language.entity.js.map +1 -0
- package/dist/backend/index.d.ts +63 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +114 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.d.ts +28 -0
- package/dist/backend/routes.d.ts.map +1 -0
- package/dist/backend/routes.js +74 -0
- package/dist/backend/routes.js.map +1 -0
- package/dist/backend/services/language-ports.d.ts +33 -0
- package/dist/backend/services/language-ports.d.ts.map +1 -0
- package/dist/backend/services/language-ports.js +70 -0
- package/dist/backend/services/language-ports.js.map +1 -0
- package/dist/backend/services/language-reference-registry.d.ts +30 -0
- package/dist/backend/services/language-reference-registry.d.ts.map +1 -0
- package/dist/backend/services/language-reference-registry.js +54 -0
- package/dist/backend/services/language-reference-registry.js.map +1 -0
- package/dist/backend/services/language-seed-service.d.ts +24 -0
- package/dist/backend/services/language-seed-service.d.ts.map +1 -0
- package/dist/backend/services/language-seed-service.js +40 -0
- package/dist/backend/services/language-seed-service.js.map +1 -0
- package/dist/backend/services/language-service.d.ts +99 -0
- package/dist/backend/services/language-service.d.ts.map +1 -0
- package/dist/backend/services/language-service.js +299 -0
- package/dist/backend/services/language-service.js.map +1 -0
- package/dist/backend/services/locale-service.d.ts +35 -0
- package/dist/backend/services/locale-service.d.ts.map +1 -0
- package/dist/backend/services/locale-service.js +88 -0
- package/dist/backend/services/locale-service.js.map +1 -0
- package/dist/manifest.d.ts +169 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +40 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260425T161557_languages_currencies_init.d.ts +13 -0
- package/dist/migrations/20260425T161557_languages_currencies_init.d.ts.map +1 -0
- package/dist/migrations/20260425T161557_languages_currencies_init.js +60 -0
- package/dist/migrations/20260425T161557_languages_currencies_init.js.map +1 -0
- package/dist/migrations/index.d.ts +27 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +27 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/languages.md +88 -0
- package/package.json +68 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocaleService — the small chunk of behaviour FR-105 calls
|
|
3
|
+
* "translation-fallback middleware".
|
|
4
|
+
*
|
|
5
|
+
* Two responsibilities:
|
|
6
|
+
* 1. `pickLocalizedValue(record, requestedLocale, defaultLocale?)` — given a
|
|
7
|
+
* `Record<localeCode, string>` (the multilingual JSONB shape used by
|
|
8
|
+
* Product / Category / CMS), return the best available string. Order:
|
|
9
|
+
* requested → default → first present → empty string.
|
|
10
|
+
* 2. `resolveRequestLocale(acceptLanguageHeader)` — pick a supported
|
|
11
|
+
* locale for the current request, with the admin-configured default
|
|
12
|
+
* as the last-resort fallback.
|
|
13
|
+
*
|
|
14
|
+
* The default locale is read once per resolver and cached for the lifetime
|
|
15
|
+
* of the service instance to avoid hitting the DB on every request; it is
|
|
16
|
+
* refreshed on `invalidateDefault()` after admin mutations.
|
|
17
|
+
*/
|
|
18
|
+
export class LocaleService {
|
|
19
|
+
languageService;
|
|
20
|
+
cachedDefaultCode = null;
|
|
21
|
+
cachedDefaultLoadedAt = 0;
|
|
22
|
+
cacheTtlMs = 60_000;
|
|
23
|
+
constructor(languageService) {
|
|
24
|
+
this.languageService = languageService;
|
|
25
|
+
}
|
|
26
|
+
pickLocalizedValue(record, requestedLocale, defaultLocale) {
|
|
27
|
+
if (!record)
|
|
28
|
+
return '';
|
|
29
|
+
if (record[requestedLocale])
|
|
30
|
+
return record[requestedLocale];
|
|
31
|
+
if (defaultLocale && record[defaultLocale])
|
|
32
|
+
return record[defaultLocale];
|
|
33
|
+
const values = Object.values(record);
|
|
34
|
+
return values[0] ?? '';
|
|
35
|
+
}
|
|
36
|
+
async getDefaultLocale() {
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
if (this.cachedDefaultCode && now - this.cachedDefaultLoadedAt < this.cacheTtlMs) {
|
|
39
|
+
return this.cachedDefaultCode;
|
|
40
|
+
}
|
|
41
|
+
const row = await this.languageService.getDefault();
|
|
42
|
+
this.cachedDefaultCode = row?.code ?? 'en-US';
|
|
43
|
+
this.cachedDefaultLoadedAt = now;
|
|
44
|
+
return this.cachedDefaultCode;
|
|
45
|
+
}
|
|
46
|
+
invalidateDefault() {
|
|
47
|
+
this.cachedDefaultLoadedAt = 0;
|
|
48
|
+
this.cachedDefaultCode = null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Resolve a single locale string from an Accept-Language header against
|
|
52
|
+
* the active language pool. Returns the configured default when nothing
|
|
53
|
+
* matches.
|
|
54
|
+
*/
|
|
55
|
+
async resolveRequestLocale(acceptLanguageHeader, activeLocales) {
|
|
56
|
+
const def = await this.getDefaultLocale();
|
|
57
|
+
if (!acceptLanguageHeader)
|
|
58
|
+
return def;
|
|
59
|
+
const candidates = parseAcceptLanguage(acceptLanguageHeader);
|
|
60
|
+
for (const cand of candidates) {
|
|
61
|
+
const exact = activeLocales.find((l) => l.toLowerCase() === cand.toLowerCase());
|
|
62
|
+
if (exact)
|
|
63
|
+
return exact;
|
|
64
|
+
// Fall back to the language part only ("en" matches "en-US").
|
|
65
|
+
const langOnly = cand.split('-')[0].toLowerCase();
|
|
66
|
+
const broad = activeLocales.find((l) => l.split('-')[0].toLowerCase() === langOnly);
|
|
67
|
+
if (broad)
|
|
68
|
+
return broad;
|
|
69
|
+
}
|
|
70
|
+
return def;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function parseAcceptLanguage(header) {
|
|
74
|
+
return header
|
|
75
|
+
.split(',')
|
|
76
|
+
.map((part) => {
|
|
77
|
+
const [tag, ...params] = part.trim().split(';');
|
|
78
|
+
const q = params
|
|
79
|
+
.map((p) => p.trim())
|
|
80
|
+
.find((p) => p.startsWith('q='))
|
|
81
|
+
?.slice(2);
|
|
82
|
+
return { tag: tag.trim(), q: q ? Number(q) : 1 };
|
|
83
|
+
})
|
|
84
|
+
.filter((c) => c.tag && !Number.isNaN(c.q))
|
|
85
|
+
.sort((a, b) => b.q - a.q)
|
|
86
|
+
.map((c) => c.tag);
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=locale-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locale-service.js","sourceRoot":"","sources":["../../../src/backend/services/locale-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Languages module — manifest backfill (Module Lifecycle, feature 018).
|
|
3
|
+
*
|
|
4
|
+
* Predates the lifecycle system; this manifest is the static record
|
|
5
|
+
* required so the module participates in the registry. No install /
|
|
6
|
+
* uninstall hook today — the module's schema is owned by earlier
|
|
7
|
+
* platform-wide migrations.
|
|
8
|
+
*/
|
|
9
|
+
export declare const manifest: {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
version: string;
|
|
13
|
+
dependencies: string[];
|
|
14
|
+
description?: string | undefined;
|
|
15
|
+
acknowledgedDependencies?: {
|
|
16
|
+
moduleId: string;
|
|
17
|
+
port: string;
|
|
18
|
+
reason: string;
|
|
19
|
+
}[] | undefined;
|
|
20
|
+
nonBindingDependencies?: {
|
|
21
|
+
moduleId: string;
|
|
22
|
+
name: string;
|
|
23
|
+
kind: "contributes-to" | "degrades-without" | "refuses-without";
|
|
24
|
+
reason: string;
|
|
25
|
+
whenAbsent?: string | undefined;
|
|
26
|
+
}[] | undefined;
|
|
27
|
+
activation?: {
|
|
28
|
+
settingCode: string;
|
|
29
|
+
default: boolean;
|
|
30
|
+
} | {
|
|
31
|
+
nonDeactivatable: true;
|
|
32
|
+
reason: string;
|
|
33
|
+
} | undefined;
|
|
34
|
+
settings?: {
|
|
35
|
+
moduleCode: string;
|
|
36
|
+
groups: {
|
|
37
|
+
code: string;
|
|
38
|
+
name: string;
|
|
39
|
+
salesChannelCodes?: string[] | undefined;
|
|
40
|
+
isSystemProtected?: boolean | undefined;
|
|
41
|
+
}[];
|
|
42
|
+
settings: {
|
|
43
|
+
code: string;
|
|
44
|
+
name: string;
|
|
45
|
+
valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
|
|
46
|
+
defaultValue: unknown;
|
|
47
|
+
description?: string | undefined;
|
|
48
|
+
groupCode?: string | undefined;
|
|
49
|
+
previousDefaultValues?: unknown[] | undefined;
|
|
50
|
+
salesChannelCodes?: string[] | undefined;
|
|
51
|
+
enumOptions?: string[] | undefined;
|
|
52
|
+
configurationType?: string | undefined;
|
|
53
|
+
hidden?: boolean | undefined;
|
|
54
|
+
}[];
|
|
55
|
+
} | undefined;
|
|
56
|
+
i18n?: {
|
|
57
|
+
bundlesDir: string;
|
|
58
|
+
} | undefined;
|
|
59
|
+
docs?: false | {
|
|
60
|
+
dir: string;
|
|
61
|
+
} | undefined;
|
|
62
|
+
demo?: false | {
|
|
63
|
+
summary: string;
|
|
64
|
+
seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
|
|
65
|
+
reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
|
|
66
|
+
after?: readonly string[] | undefined;
|
|
67
|
+
package?: string | undefined;
|
|
68
|
+
} | undefined;
|
|
69
|
+
actions?: {
|
|
70
|
+
id: string;
|
|
71
|
+
labelKey: string;
|
|
72
|
+
icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
|
|
73
|
+
targetRoute: string;
|
|
74
|
+
keywords: string[];
|
|
75
|
+
weight: number;
|
|
76
|
+
descriptionKey?: string | undefined;
|
|
77
|
+
requiredPermission?: string | undefined;
|
|
78
|
+
}[] | undefined;
|
|
79
|
+
permissions?: {
|
|
80
|
+
code: string;
|
|
81
|
+
label: string;
|
|
82
|
+
module?: string | undefined;
|
|
83
|
+
description?: string | undefined;
|
|
84
|
+
requires?: string[] | undefined;
|
|
85
|
+
}[] | undefined;
|
|
86
|
+
transactionalEmails?: {
|
|
87
|
+
code: string;
|
|
88
|
+
name: string;
|
|
89
|
+
variables: {
|
|
90
|
+
key: string;
|
|
91
|
+
label: string;
|
|
92
|
+
sampleValue?: string | undefined;
|
|
93
|
+
description?: string | undefined;
|
|
94
|
+
}[];
|
|
95
|
+
description?: string | undefined;
|
|
96
|
+
group?: string | undefined;
|
|
97
|
+
}[] | undefined;
|
|
98
|
+
capabilities?: string[] | undefined;
|
|
99
|
+
exclusiveCapabilities?: {
|
|
100
|
+
key: string;
|
|
101
|
+
errorCode: string;
|
|
102
|
+
}[] | undefined;
|
|
103
|
+
errorCodes?: {
|
|
104
|
+
code: string;
|
|
105
|
+
tokens?: string[] | undefined;
|
|
106
|
+
}[] | undefined;
|
|
107
|
+
blocks?: {
|
|
108
|
+
name: string;
|
|
109
|
+
labelKey: string;
|
|
110
|
+
category: string;
|
|
111
|
+
contexts: ("invoice" | "email" | "cms" | "newsletter")[];
|
|
112
|
+
fields: Record<string, {
|
|
113
|
+
type: "number" | "object" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "uuid" | "richtext";
|
|
114
|
+
label?: string | undefined;
|
|
115
|
+
required?: boolean | undefined;
|
|
116
|
+
options?: {
|
|
117
|
+
label: string;
|
|
118
|
+
value: string | number;
|
|
119
|
+
}[] | undefined;
|
|
120
|
+
refKind?: string | undefined;
|
|
121
|
+
}>;
|
|
122
|
+
descriptionKey?: string | undefined;
|
|
123
|
+
defaultProps?: Record<string, unknown> | undefined;
|
|
124
|
+
responsiveFields?: string[] | undefined;
|
|
125
|
+
previewIcon?: string | undefined;
|
|
126
|
+
weight?: number | undefined;
|
|
127
|
+
}[] | undefined;
|
|
128
|
+
blockCategories?: {
|
|
129
|
+
key: string;
|
|
130
|
+
titleKey: string;
|
|
131
|
+
contexts: ("invoice" | "email" | "cms" | "newsletter")[];
|
|
132
|
+
weight?: number | undefined;
|
|
133
|
+
visible?: boolean | undefined;
|
|
134
|
+
}[] | undefined;
|
|
135
|
+
env?: {
|
|
136
|
+
name: string;
|
|
137
|
+
describes: {
|
|
138
|
+
en: string;
|
|
139
|
+
pl: string;
|
|
140
|
+
};
|
|
141
|
+
requirement: {
|
|
142
|
+
kind: "required";
|
|
143
|
+
} | {
|
|
144
|
+
kind: "requiredWhen";
|
|
145
|
+
input: string;
|
|
146
|
+
equals: string;
|
|
147
|
+
} | {
|
|
148
|
+
kind: "optional";
|
|
149
|
+
without: {
|
|
150
|
+
en: string;
|
|
151
|
+
pl: string;
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
|
+
secret: boolean;
|
|
155
|
+
generable: boolean;
|
|
156
|
+
owner: {
|
|
157
|
+
kind: "platform";
|
|
158
|
+
} | {
|
|
159
|
+
kind: "application";
|
|
160
|
+
application: "admin" | "backend" | "storefront";
|
|
161
|
+
} | {
|
|
162
|
+
kind: "module";
|
|
163
|
+
moduleId: string;
|
|
164
|
+
};
|
|
165
|
+
consumers: ("admin" | "backend" | "storefront")[];
|
|
166
|
+
addressOf: "admin" | "backend" | "storefront" | null;
|
|
167
|
+
}[] | undefined;
|
|
168
|
+
};
|
|
169
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BnB,CAAC"}
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { defineModuleManifest } from '@endora-commerce/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* Languages module — manifest backfill (Module Lifecycle, feature 018).
|
|
4
|
+
*
|
|
5
|
+
* Predates the lifecycle system; this manifest is the static record
|
|
6
|
+
* required so the module participates in the registry. No install /
|
|
7
|
+
* uninstall hook today — the module's schema is owned by earlier
|
|
8
|
+
* platform-wide migrations.
|
|
9
|
+
*/
|
|
10
|
+
export const manifest = defineModuleManifest({
|
|
11
|
+
id: 'languages',
|
|
12
|
+
docs: { dir: 'docs' },
|
|
13
|
+
name: 'Languages',
|
|
14
|
+
description: 'Storefront/admin language catalog and per-channel locale routing.',
|
|
15
|
+
version: '1.0.0',
|
|
16
|
+
// `auth` owns the `requireAdmin` port the admin routes are gated by;
|
|
17
|
+
// `currencies` owns `currencyReadPort`. Feature 072 made both container
|
|
18
|
+
// resolutions.
|
|
19
|
+
//
|
|
20
|
+
// The `currencies` edge narrowed on 2026-08-29 and did not go away: the four
|
|
21
|
+
// `/api/v1/admin/currencies*` routes this module served, and the
|
|
22
|
+
// `currencyAdminPort` behind them, moved to their owner. What still reaches
|
|
23
|
+
// across is `GET /api/v1/i18n/config`, which answers with both catalogues and
|
|
24
|
+
// both defaults in one public payload — composition rather than ownership,
|
|
25
|
+
// and one read rather than a write surface.
|
|
26
|
+
dependencies: ['auth', 'currencies'],
|
|
27
|
+
// Feature 074 (Constitution XVII), test C3 — platform primitive. The flag
|
|
28
|
+
// used to rest on a two-hop walk of somebody else's `dependencies`
|
|
29
|
+
// (`organizations` → `dictionaries` → here); ruling 2 removes that as a
|
|
30
|
+
// ground entirely. This module's own is that it owns the locale scope: every
|
|
31
|
+
// localized surface, every translation bundle and every per-channel routing
|
|
32
|
+
// decision resolves against the language catalogue, so its absence is not a
|
|
33
|
+
// reduced platform but an unresolvable one.
|
|
34
|
+
activation: {
|
|
35
|
+
nonDeactivatable: true,
|
|
36
|
+
reason: 'The locale scope every localized surface and every translation bundle resolves ' +
|
|
37
|
+
'against.',
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAElE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAoB,CAAC;IAC3C,EAAE,EAAE,WAAW;IACf,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE;IACrB,IAAI,EAAE,WAAW;IACjB,WAAW,EACT,mEAAmE;IACrE,OAAO,EAAE,OAAO;IAChB,qEAAqE;IACrE,wEAAwE;IACxE,eAAe;IACf,EAAE;IACF,6EAA6E;IAC7E,iEAAiE;IACjE,4EAA4E;IAC5E,8EAA8E;IAC9E,2EAA2E;IAC3E,4CAA4C;IAC5C,YAAY,EAAE,CAAC,MAAM,EAAE,YAAY,CAAC;IACpC,0EAA0E;IAC1E,mEAAmE;IACnE,wEAAwE;IACxE,6EAA6E;IAC7E,4EAA4E;IAC5E,4EAA4E;IAC5E,4CAA4C;IAC5C,UAAU,EAAE;QACV,gBAAgB,EAAE,IAAI;QACtB,MAAM,EACJ,iFAAiF;YACjF,UAAU;KACb;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Languages + currencies init (T238 / FR-105). Two configuration tables
|
|
4
|
+
* with a partial unique index pinning at most one default row each.
|
|
5
|
+
*
|
|
6
|
+
* Co-located in the `languages` module folder to keep the migration
|
|
7
|
+
* sequence numerical without inventing a third placeholder module.
|
|
8
|
+
*/
|
|
9
|
+
export declare class Migration20260425T161557LanguagesCurrenciesInit extends Migration {
|
|
10
|
+
up(): Promise<void>;
|
|
11
|
+
down(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=20260425T161557_languages_currencies_init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260425T161557_languages_currencies_init.d.ts","sourceRoot":"","sources":["../../src/migrations/20260425T161557_languages_currencies_init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;GAMG;AACH,qBAAa,+CAAgD,SAAQ,SAAS;IAC7D,EAAE,IAAI,OAAO,CAAC,IAAI,CAAC;IAwDnB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAIrC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Migration } from '@mikro-orm/migrations';
|
|
2
|
+
/**
|
|
3
|
+
* Languages + currencies init (T238 / FR-105). Two configuration tables
|
|
4
|
+
* with a partial unique index pinning at most one default row each.
|
|
5
|
+
*
|
|
6
|
+
* Co-located in the `languages` module folder to keep the migration
|
|
7
|
+
* sequence numerical without inventing a third placeholder module.
|
|
8
|
+
*/
|
|
9
|
+
export class Migration20260425T161557LanguagesCurrenciesInit extends Migration {
|
|
10
|
+
async up() {
|
|
11
|
+
this.addSql(`
|
|
12
|
+
create table "languages" (
|
|
13
|
+
"code" varchar(12) not null,
|
|
14
|
+
"label" varchar(64) not null,
|
|
15
|
+
"is_default" boolean not null default false,
|
|
16
|
+
"is_active" boolean not null default true,
|
|
17
|
+
"sort_order" int not null default 0,
|
|
18
|
+
"created_at" timestamptz not null,
|
|
19
|
+
"updated_at" timestamptz not null,
|
|
20
|
+
constraint "languages_pkey" primary key ("code")
|
|
21
|
+
);
|
|
22
|
+
`);
|
|
23
|
+
this.addSql('create unique index "uniq_languages_one_default" on "languages" ("is_default") where "is_default" = true;');
|
|
24
|
+
this.addSql('create index "languages_is_active_index" on "languages" ("is_active");');
|
|
25
|
+
this.addSql(`
|
|
26
|
+
create table "currencies" (
|
|
27
|
+
"code" varchar(3) not null,
|
|
28
|
+
"label" varchar(64) not null,
|
|
29
|
+
"symbol" varchar(8) not null,
|
|
30
|
+
"is_default" boolean not null default false,
|
|
31
|
+
"is_active" boolean not null default true,
|
|
32
|
+
"sort_order" int not null default 0,
|
|
33
|
+
"created_at" timestamptz not null,
|
|
34
|
+
"updated_at" timestamptz not null,
|
|
35
|
+
constraint "currencies_pkey" primary key ("code")
|
|
36
|
+
);
|
|
37
|
+
`);
|
|
38
|
+
this.addSql('create unique index "uniq_currencies_one_default" on "currencies" ("is_default") where "is_default" = true;');
|
|
39
|
+
this.addSql('create index "currencies_is_active_index" on "currencies" ("is_active");');
|
|
40
|
+
// Bootstrap with the project defaults so quickstart works without an
|
|
41
|
+
// additional admin step. en-US + PLN match the existing seed data.
|
|
42
|
+
// Customer-facing label / symbol values use Postgres U&'…' Unicode
|
|
43
|
+
// literals so the source file stays ASCII-only (Principle VIII's
|
|
44
|
+
// engineering-artifact constraint), while the runtime row reflects
|
|
45
|
+
// exactly what the storefront should render.
|
|
46
|
+
this.addSql(`insert into "languages" ("code", "label", "is_default", "is_active", "sort_order", "created_at", "updated_at")
|
|
47
|
+
values
|
|
48
|
+
('en-US', 'English (US)', true, true, 0, now(), now()),
|
|
49
|
+
('pl-PL', 'Polski', false, true, 1, now(), now());`);
|
|
50
|
+
this.addSql(`insert into "currencies" ("code", "label", "symbol", "is_default", "is_active", "sort_order", "created_at", "updated_at")
|
|
51
|
+
values
|
|
52
|
+
('PLN', 'Polish zloty', U&'z\\0142', true, true, 0, now(), now()),
|
|
53
|
+
('EUR', 'Euro', U&'\\20AC', false, true, 1, now(), now());`);
|
|
54
|
+
}
|
|
55
|
+
async down() {
|
|
56
|
+
this.addSql('drop table if exists "currencies" cascade;');
|
|
57
|
+
this.addSql('drop table if exists "languages" cascade;');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=20260425T161557_languages_currencies_init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20260425T161557_languages_currencies_init.js","sourceRoot":"","sources":["../../src/migrations/20260425T161557_languages_currencies_init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,OAAO,+CAAgD,SAAQ,SAAS;IACnE,KAAK,CAAC,EAAE;QACf,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;KAWX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,2GAA2G,CAC5G,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,wEAAwE,CAAC,CAAC;QAEtF,IAAI,CAAC,MAAM,CAAC;;;;;;;;;;;;KAYX,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CACT,6GAA6G,CAC9G,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,0EAA0E,CAAC,CAAC;QAExF,qEAAqE;QACrE,mEAAmE;QACnE,mEAAmE;QACnE,iEAAiE;QACjE,mEAAmE;QACnE,6CAA6C;QAC7C,IAAI,CAAC,MAAM,CACT;;;4DAGsD,CACvD,CAAC;QACF,IAAI,CAAC,MAAM,CACT;;;oEAG8D,CAC/D,CAAC;IACJ,CAAC;IAEQ,KAAK,CAAC,IAAI;QACjB,IAAI,CAAC,MAAM,CAAC,4CAA4C,CAAC,CAAC;QAC1D,IAAI,CAAC,MAAM,CAAC,2CAA2C,CAAC,CAAC;IAC3D,CAAC;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `./migrations` subpath — every migration class this module owns, as one
|
|
3
|
+
* ordered `migrations` array.
|
|
4
|
+
*
|
|
5
|
+
* The array is what the platform reads when this module is **installed**:
|
|
6
|
+
* `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
|
|
7
|
+
* the package outright when it is absent (D-168).
|
|
8
|
+
*
|
|
9
|
+
* **One class.** It creates the `languages` table and the `currencies` table
|
|
10
|
+
* together — the two shipped as one migration long before either module was a
|
|
11
|
+
* package — so `currencies` owns rows this file creates and ships no migration
|
|
12
|
+
* of its own. That is a fact about history, not an ordering rule: a timestamp
|
|
13
|
+
* orders this module's own migrations and nothing else (feature 081), and where
|
|
14
|
+
* this block sits relative to every other module's is decided by the manifest
|
|
15
|
+
* `dependencies` graph.
|
|
16
|
+
*
|
|
17
|
+
* The **named** export stays beside the array, and the asymmetry with
|
|
18
|
+
* `./backend` — which publishes an array and no named class (D-168) — is
|
|
19
|
+
* deliberate. `db/migrations-registry.generated.ts` imports the class by name
|
|
20
|
+
* from this specifier, and a migration class name is contract in a way an
|
|
21
|
+
* entity class name is not: `mikro_orm_migrations` persists it, so it is a
|
|
22
|
+
* string every already-migrated database holds.
|
|
23
|
+
*/
|
|
24
|
+
import { Migration20260425T161557LanguagesCurrenciesInit } from './20260425T161557_languages_currencies_init.js';
|
|
25
|
+
export declare const migrations: (typeof Migration20260425T161557LanguagesCurrenciesInit)[];
|
|
26
|
+
export { Migration20260425T161557LanguagesCurrenciesInit };
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,+CAA+C,EAAE,MAAM,gDAAgD,CAAC;AAEjH,eAAO,MAAM,UAAU,4DAAoD,CAAC;AAE5E,OAAO,EAAE,+CAA+C,EAAE,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `./migrations` subpath — every migration class this module owns, as one
|
|
3
|
+
* ordered `migrations` array.
|
|
4
|
+
*
|
|
5
|
+
* The array is what the platform reads when this module is **installed**:
|
|
6
|
+
* `src/packages/package-runtime.ts` takes `exported['migrations']` and refuses
|
|
7
|
+
* the package outright when it is absent (D-168).
|
|
8
|
+
*
|
|
9
|
+
* **One class.** It creates the `languages` table and the `currencies` table
|
|
10
|
+
* together — the two shipped as one migration long before either module was a
|
|
11
|
+
* package — so `currencies` owns rows this file creates and ships no migration
|
|
12
|
+
* of its own. That is a fact about history, not an ordering rule: a timestamp
|
|
13
|
+
* orders this module's own migrations and nothing else (feature 081), and where
|
|
14
|
+
* this block sits relative to every other module's is decided by the manifest
|
|
15
|
+
* `dependencies` graph.
|
|
16
|
+
*
|
|
17
|
+
* The **named** export stays beside the array, and the asymmetry with
|
|
18
|
+
* `./backend` — which publishes an array and no named class (D-168) — is
|
|
19
|
+
* deliberate. `db/migrations-registry.generated.ts` imports the class by name
|
|
20
|
+
* from this specifier, and a migration class name is contract in a way an
|
|
21
|
+
* entity class name is not: `mikro_orm_migrations` persists it, so it is a
|
|
22
|
+
* string every already-migrated database holds.
|
|
23
|
+
*/
|
|
24
|
+
import { Migration20260425T161557LanguagesCurrenciesInit } from './20260425T161557_languages_currencies_init.js';
|
|
25
|
+
export const migrations = [Migration20260425T161557LanguagesCurrenciesInit];
|
|
26
|
+
export { Migration20260425T161557LanguagesCurrenciesInit };
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,+CAA+C,EAAE,MAAM,gDAAgD,CAAC;AAEjH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,+CAA+C,CAAC,CAAC;AAE5E,OAAO,EAAE,+CAA+C,EAAE,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: languages
|
|
3
|
+
description: Pool of supported BCP-47 language tags + translation-fallback helper
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# `languages`
|
|
7
|
+
|
|
8
|
+
The installation-wide pool of supported BCP-47 language tags. Owns the
|
|
9
|
+
public `i18n/config` read path that storefront + admin consume to render
|
|
10
|
+
their language pickers, plus a small `LocaleService` that implements the
|
|
11
|
+
translation-fallback rule.
|
|
12
|
+
|
|
13
|
+
## Public surface
|
|
14
|
+
|
|
15
|
+
| Verb + Path | Audience | Purpose |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `GET /api/v1/i18n/config` | storefront / admin | Active languages + currencies + the configured defaults |
|
|
18
|
+
| `GET /api/v1/admin/languages` | admin | Full list, including inactive rows |
|
|
19
|
+
| `PUT /api/v1/admin/languages/:code` | admin | Upsert |
|
|
20
|
+
| `POST /api/v1/admin/languages/:code/default` | admin | Promote to default (atomically demotes the prior default) |
|
|
21
|
+
| `DELETE /api/v1/admin/languages/:code` | admin | Remove (rejected for the default) |
|
|
22
|
+
|
|
23
|
+
The currency catalogue has the same shape under `/api/v1/admin/currencies`, and
|
|
24
|
+
those routes are **`currencies`'** — see [currencies](./currencies.md). They were
|
|
25
|
+
registered here until 2026-08-29, on `catalog:write`, serving another module's
|
|
26
|
+
table for no caller in this repository. What this module still composes is
|
|
27
|
+
`GET /api/v1/i18n/config`, which answers with both catalogues and both defaults
|
|
28
|
+
in one public payload and reads the currency half over `currencyReadPort`.
|
|
29
|
+
|
|
30
|
+
The four admin language routes above enforce `catalog:write`. That is a
|
|
31
|
+
neighbourhood claim of the same kind and has not been repaired.
|
|
32
|
+
|
|
33
|
+
## Defaults
|
|
34
|
+
|
|
35
|
+
Exactly zero or one row in `languages` has `is_default = true`,
|
|
36
|
+
enforced by a partial unique index on `(is_default) WHERE is_default =
|
|
37
|
+
true`. Setting a new default runs the demote-then-promote pair inside one
|
|
38
|
+
MikroORM transaction so the partial unique index is never violated
|
|
39
|
+
mid-flight.
|
|
40
|
+
|
|
41
|
+
The `LanguageService.setDefault()` rejects rows where `isActive=false`
|
|
42
|
+
(`409 VALIDATION_FAILED`), and the `remove()` path refuses to delete a
|
|
43
|
+
row that is currently the default.
|
|
44
|
+
|
|
45
|
+
## Bootstrap
|
|
46
|
+
|
|
47
|
+
Migration 012 inserts two rows so quickstart works without an admin step:
|
|
48
|
+
- `en-US` — default, active
|
|
49
|
+
- `pl-PL` — active
|
|
50
|
+
|
|
51
|
+
The customer-facing `label` and `symbol` (currencies) values are written
|
|
52
|
+
with Postgres `U&'…'` Unicode literals so the migration source file stays
|
|
53
|
+
ASCII-only (engineering artifacts stay English-only and ASCII-only;
|
|
54
|
+
the runtime row reflects what the storefront should render).
|
|
55
|
+
|
|
56
|
+
## Translation-fallback (`LocaleService`)
|
|
57
|
+
|
|
58
|
+
`LocaleService.pickLocalizedValue(record, requestedLocale, defaultLocale?)`
|
|
59
|
+
implements the lookup chain:
|
|
60
|
+
|
|
61
|
+
1. requested locale, if present in the record.
|
|
62
|
+
2. configured default locale, if supplied and present.
|
|
63
|
+
3. first present value in the record.
|
|
64
|
+
4. empty string.
|
|
65
|
+
|
|
66
|
+
`resolveRequestLocale(acceptLanguageHeader, activeLocales)` parses an
|
|
67
|
+
`Accept-Language` header (q-weighted) and returns the highest-priority
|
|
68
|
+
match from the active language pool, with a language-only fallback so
|
|
69
|
+
`en-GB` matches `en-US`. Falls back to the configured default when nothing
|
|
70
|
+
matches.
|
|
71
|
+
|
|
72
|
+
The default-locale lookup is cached for 60 seconds; admin mutations call
|
|
73
|
+
`invalidateDefault()` so the cache flushes immediately after a change.
|
|
74
|
+
|
|
75
|
+
## Entities
|
|
76
|
+
|
|
77
|
+
`Language` — natural primary key on the BCP-47 code; `label`,
|
|
78
|
+
`isDefault`, `isActive`, `sortOrder`.
|
|
79
|
+
|
|
80
|
+
## Extension points
|
|
81
|
+
|
|
82
|
+
- **Per-Sales-Channel default** — when a Sales Channel ships its own
|
|
83
|
+
language, hook the resolver before
|
|
84
|
+
`LocaleService.resolveRequestLocale()` and use the channel's default
|
|
85
|
+
instead of the global one.
|
|
86
|
+
- **Translation pull/push** — emit a domain event when a localized field
|
|
87
|
+
changes and let an integration consume it for an external translation
|
|
88
|
+
workflow.
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@endora-commerce/mod-languages",
|
|
3
|
+
"version": "0.100.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"description": "Storefront/admin language catalog and per-channel locale routing.",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"endora": {
|
|
9
|
+
"type": "module",
|
|
10
|
+
"id": "languages"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/endora-commerce/endora-commerce.git",
|
|
15
|
+
"directory": "packages/modules/languages"
|
|
16
|
+
},
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/manifest.d.ts",
|
|
23
|
+
"default": "./dist/manifest.js"
|
|
24
|
+
},
|
|
25
|
+
"./backend": {
|
|
26
|
+
"types": "./dist/backend/index.d.ts",
|
|
27
|
+
"default": "./dist/backend/index.js"
|
|
28
|
+
},
|
|
29
|
+
"./migrations": {
|
|
30
|
+
"types": "./dist/migrations/index.d.ts",
|
|
31
|
+
"default": "./dist/migrations/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./package.json": "./package.json"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"docs"
|
|
38
|
+
],
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=22.18.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"@mikro-orm/core": "^6",
|
|
44
|
+
"@mikro-orm/migrations": "^6",
|
|
45
|
+
"@mikro-orm/postgresql": "^6",
|
|
46
|
+
"fastify": "^5",
|
|
47
|
+
"@endora-commerce/contracts": "0.100.0",
|
|
48
|
+
"@endora-commerce/platform": "0.100.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@fastify/type-provider-zod": "^1.0.0",
|
|
52
|
+
"@mikro-orm/core": "^6.6.13",
|
|
53
|
+
"@mikro-orm/migrations": "^6.6.13",
|
|
54
|
+
"@mikro-orm/postgresql": "^6.6.13",
|
|
55
|
+
"@types/node": "^22.9.0",
|
|
56
|
+
"fastify": "^5.12.5",
|
|
57
|
+
"typescript": "^5.9.3",
|
|
58
|
+
"vitest": "^4.1.11",
|
|
59
|
+
"@endora-commerce/contracts": "0.100.0",
|
|
60
|
+
"@endora-commerce/platform": "0.100.0"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsc -p tsconfig.build.json",
|
|
64
|
+
"typecheck": "tsc -p tsconfig.json",
|
|
65
|
+
"lint": "eslint src",
|
|
66
|
+
"test": "vitest run"
|
|
67
|
+
}
|
|
68
|
+
}
|