@sveltebase/i18n 0.1.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.
@@ -0,0 +1,74 @@
1
+ import * as use_intl_core from 'use-intl/core';
2
+ import { State, PersistentState } from '@svelte-essentials/state';
3
+ import { z } from 'zod';
4
+
5
+ type MessageValue = string | {
6
+ [key: string]: MessageValue;
7
+ };
8
+ type Messages = {
9
+ [key: string]: MessageValue;
10
+ };
11
+ type LanguageDefinition<TLocale extends string = string, TMessages extends Messages = Messages> = {
12
+ code: TLocale;
13
+ label: string;
14
+ messages: TMessages;
15
+ };
16
+ type FormatOptions = {
17
+ preset?: "default" | "custom" | "birthday" | "month" | "timestring" | "full";
18
+ withTime?: boolean;
19
+ };
20
+ declare function getLanguage<const TLanguages extends readonly LanguageDefinition[]>(languages: TLanguages, locale: TLanguages[number]["code"], fallbackLocale?: TLanguages[number]["code"]): LanguageDefinition<string, Messages>;
21
+ declare function createLocaleTranslator<const TLanguages extends readonly LanguageDefinition[]>(languages: TLanguages, locale: TLanguages[number]["code"], fallbackLocale?: TLanguages[number]["code"]): use_intl_core._Translator<Messages, never>;
22
+ declare function createLocaleFormatter<TLocale extends string>(locale: TLocale, timeZone?: string): {
23
+ dateTime: {
24
+ (value: Date | number, options?: use_intl_core.DateTimeFormatOptions): string;
25
+ (value: Date | number, format?: string, options?: use_intl_core.DateTimeFormatOptions): string;
26
+ };
27
+ number: {
28
+ (value: number | bigint, options?: use_intl_core.NumberFormatOptions): string;
29
+ (value: number | bigint, format?: string, options?: use_intl_core.NumberFormatOptions): string;
30
+ };
31
+ relativeTime: {
32
+ (date: number | Date, now?: use_intl_core.RelativeTimeFormatOptions["now"]): string;
33
+ (date: number | Date, options?: use_intl_core.RelativeTimeFormatOptions): string;
34
+ };
35
+ list: {
36
+ <Value extends string | ReactElement<unknown, any>>(value: Iterable<Value>, options?: Intl.ListFormatOptions): Value extends string ? string : Iterable<ReactElement>;
37
+ <Value extends string | ReactElement<unknown, any>>(value: Iterable<Value>, format?: string, options?: Intl.ListFormatOptions): Value extends string ? string : Iterable<ReactElement>;
38
+ };
39
+ dateTimeRange: {
40
+ (start: Date | number, end: Date | number, options?: use_intl_core.DateTimeFormatOptions): string;
41
+ (start: Date | number, end: Date | number, format?: string, options?: use_intl_core.DateTimeFormatOptions): string;
42
+ };
43
+ };
44
+ declare function createFormatForLocale<const TLanguages extends readonly LanguageDefinition[]>(languages: TLanguages, locale: TLanguages[number]["code"], fallbackLocale?: TLanguages[number]["code"], timeZone?: string): (value?: Date | number | string, options?: FormatOptions) => string | undefined;
45
+
46
+ type LocaleSchema<TLocale extends string> = z.ZodType<TLocale>;
47
+ type LocaleState<TLocale extends string> = State<TLocale> | PersistentState<LocaleSchema<TLocale>>;
48
+ interface CreateI18nOptions<TLanguages extends readonly LanguageDefinition[], TFormat> {
49
+ languages: TLanguages;
50
+ fallbackLocale: TLanguages[number]["code"];
51
+ signatureLocale?: TLanguages[number]["code"];
52
+ storageKey?: string;
53
+ timeZone?: string;
54
+ createFormat(args: {
55
+ locale: TLanguages[number]["code"];
56
+ fallbackLocale: TLanguages[number]["code"];
57
+ signatureLocale?: TLanguages[number]["code"];
58
+ languages: TLanguages;
59
+ timeZone?: string;
60
+ }): TFormat;
61
+ }
62
+ declare function defineLanguages<const TLanguages extends readonly LanguageDefinition[]>(languages: TLanguages): TLanguages;
63
+ declare function createI18n<const TLanguages extends readonly LanguageDefinition[], TFormat>(options: CreateI18nOptions<TLanguages, TFormat>): {
64
+ languages: TLanguages;
65
+ fallbackLocale: TLanguages[number]["code"];
66
+ signatureLocale: TLanguages[number]["code"] | undefined;
67
+ localeState: LocaleState<TLanguages[number]["code"]>;
68
+ setLocale: (locale: TLanguages[number]["code"]) => void;
69
+ getTranslations: (locale?: TLanguages[number]["code"]) => use_intl_core._Translator<Messages, never>;
70
+ getFormat: (locale?: TLanguages[number]["code"]) => TFormat;
71
+ getLanguage: (locale?: TLanguages[number]["code"]) => LanguageDefinition<string, Messages>;
72
+ };
73
+
74
+ export { type FormatOptions, type LanguageDefinition, type MessageValue, type Messages, createFormatForLocale, createI18n, createLocaleFormatter, createLocaleTranslator, defineLanguages, getLanguage };
package/dist/index.js ADDED
@@ -0,0 +1,217 @@
1
+ // src/create-i18n.ts
2
+ import { PersistentState, State } from "@svelte-essentials/state";
3
+ import { z } from "zod";
4
+
5
+ // src/utils.ts
6
+ import { createFormatter, createTranslator } from "use-intl/core";
7
+ import { SvelteDate } from "svelte/reactivity";
8
+ import {
9
+ differenceInMinutes,
10
+ format as formatTimeWithDateFns,
11
+ isThisWeek,
12
+ isThisYear,
13
+ isToday,
14
+ isYesterday
15
+ } from "date-fns";
16
+ var UZ_WEEKDAYS = [
17
+ "Yakshanba",
18
+ "Dushanba",
19
+ "Seshanba",
20
+ "Chorshanba",
21
+ "Payshanba",
22
+ "Juma",
23
+ "Shanba"
24
+ ];
25
+ var UZ_MONTHS = [
26
+ "Yanvar",
27
+ "Fevral",
28
+ "Mart",
29
+ "Aprel",
30
+ "May",
31
+ "Iyun",
32
+ "Iyul",
33
+ "Avgust",
34
+ "Sentabr",
35
+ "Oktabr",
36
+ "Noyabr",
37
+ "Dekabr"
38
+ ];
39
+ function getLocaleCodes(languages) {
40
+ return languages.map((language) => language.code);
41
+ }
42
+ function getLanguage(languages, locale, fallbackLocale) {
43
+ return languages.find((language) => language.code === locale) ?? (fallbackLocale ? languages.find((language) => language.code === fallbackLocale) : void 0) ?? languages[0];
44
+ }
45
+ function createLocaleTranslator(languages, locale, fallbackLocale) {
46
+ const language = getLanguage(languages, locale, fallbackLocale);
47
+ return createTranslator({
48
+ locale: language.code,
49
+ messages: language.messages
50
+ });
51
+ }
52
+ function createLocaleFormatter(locale, timeZone = "Asia/Tashkent") {
53
+ return createFormatter({
54
+ locale,
55
+ timeZone,
56
+ now: new SvelteDate()
57
+ });
58
+ }
59
+ function toDate(value) {
60
+ return value instanceof Date ? value : new SvelteDate(value);
61
+ }
62
+ function formatTime(value) {
63
+ return formatTimeWithDateFns(value, "HH:mm");
64
+ }
65
+ function formatUzDate(value, withYear, withTime = false, suffix = "") {
66
+ let text = withYear ? `${value.getFullYear()}-yil, ${value.getDate()}-${UZ_MONTHS[value.getMonth()].toLowerCase()}` : `${value.getDate()}-${UZ_MONTHS[value.getMonth()].toLowerCase()}`;
67
+ if (withTime) {
68
+ text += `, ${formatTime(value)}${suffix}`;
69
+ }
70
+ return text;
71
+ }
72
+ function formatUzMonth(value, withYear) {
73
+ return withYear ? `${value.getFullYear()}-yil, ${UZ_MONTHS[value.getMonth()]}` : UZ_MONTHS[value.getMonth()];
74
+ }
75
+ function createFormatForLocale(languages, locale, fallbackLocale, timeZone = "Asia/Tashkent") {
76
+ const formatter = createLocaleFormatter(locale, timeZone);
77
+ const t = createLocaleTranslator(languages, locale, fallbackLocale);
78
+ return (value, options) => {
79
+ if (!value) {
80
+ return void 0;
81
+ }
82
+ const preset = options?.preset ?? "default";
83
+ const withTime = options?.withTime ?? false;
84
+ if (typeof value === "string" && preset === "timestring") {
85
+ const [hours = "0", minutes = "0", seconds = "0"] = value.split(":");
86
+ const timeDate = new SvelteDate();
87
+ timeDate.setHours(Number(hours), Number(minutes), Number(seconds), 0);
88
+ return formatter.dateTime(timeDate, {
89
+ hour: "numeric",
90
+ minute: "numeric"
91
+ });
92
+ }
93
+ const date = toDate(value);
94
+ if (preset === "full") {
95
+ return locale === "uz" ? formatUzDate(date, true, withTime) : formatter.dateTime(date, {
96
+ year: "numeric",
97
+ month: "long",
98
+ day: "numeric",
99
+ ...withTime ? { hour: "numeric", minute: "numeric" } : {}
100
+ });
101
+ }
102
+ if (preset === "custom") {
103
+ const now = new SvelteDate();
104
+ const diffMinutes = differenceInMinutes(now, date);
105
+ if (diffMinutes < 1) return t("just-now");
106
+ if (diffMinutes < 60) return t("minutes-ago", { minutes: diffMinutes });
107
+ if (isToday(date)) return t("today-at", { time: formatTime(date) });
108
+ if (isYesterday(date)) return t("yesterday-at", { time: formatTime(date) });
109
+ if (isThisWeek(date, { weekStartsOn: 1 })) {
110
+ return locale === "uz" ? `${UZ_WEEKDAYS[date.getDay()]}, ${formatTime(date)} da` : formatter.dateTime(date, {
111
+ weekday: "long",
112
+ hour: "numeric",
113
+ minute: "numeric"
114
+ });
115
+ }
116
+ if (isThisYear(date)) {
117
+ return locale === "uz" ? formatUzDate(date, false, withTime, " da") : formatter.dateTime(date, {
118
+ month: "long",
119
+ day: "numeric",
120
+ ...withTime ? { hour: "numeric", minute: "numeric" } : {}
121
+ });
122
+ }
123
+ return locale === "uz" ? formatUzDate(date, true, withTime, " da") : formatter.dateTime(date, {
124
+ year: "numeric",
125
+ month: "long",
126
+ day: "numeric",
127
+ ...withTime ? { hour: "numeric", minute: "numeric" } : {}
128
+ });
129
+ }
130
+ if (preset === "month") {
131
+ if (isThisYear(date)) {
132
+ return locale === "uz" ? formatUzMonth(date, false) : formatter.dateTime(date, { month: "long" });
133
+ }
134
+ return locale === "uz" ? formatUzMonth(date, true) : formatter.dateTime(date, { month: "long", year: "numeric" });
135
+ }
136
+ if (preset === "birthday") {
137
+ return locale === "uz" ? `${date.getFullYear()}-yil, ${date.getDate()}-${UZ_MONTHS[date.getMonth()]}` : formatter.dateTime(date, {
138
+ year: "numeric",
139
+ month: "long",
140
+ day: "numeric"
141
+ });
142
+ }
143
+ if (isThisYear(date)) {
144
+ return locale === "uz" ? formatUzDate(date, false, withTime, " da") : formatter.dateTime(date, {
145
+ month: "long",
146
+ day: "numeric",
147
+ ...withTime ? { hour: "numeric", minute: "numeric" } : {}
148
+ });
149
+ }
150
+ return locale === "uz" ? formatUzDate(date, true, withTime, " da") : formatter.dateTime(date, {
151
+ year: "numeric",
152
+ month: "long",
153
+ day: "numeric",
154
+ ...withTime ? { hour: "numeric", minute: "numeric" } : {}
155
+ });
156
+ };
157
+ }
158
+
159
+ // src/create-i18n.ts
160
+ function defineLanguages(languages) {
161
+ return languages;
162
+ }
163
+ function createI18n(options) {
164
+ const { languages, fallbackLocale, signatureLocale, storageKey, timeZone, createFormat } = options;
165
+ const localeCodes = getLocaleCodes(languages);
166
+ const localeSchema = z.string().transform((value, context) => {
167
+ const nextLocale = value || fallbackLocale;
168
+ if (localeCodes.includes(nextLocale)) {
169
+ return nextLocale;
170
+ }
171
+ context.addIssue({
172
+ code: "custom",
173
+ message: `Invalid locale "${String(value)}"`
174
+ });
175
+ return z.NEVER;
176
+ });
177
+ const localeState = storageKey ? new PersistentState(storageKey, localeSchema) : new State(fallbackLocale);
178
+ function getLocale(locale) {
179
+ return locale ?? localeState.current;
180
+ }
181
+ function setLocale(locale) {
182
+ localeState.current = localeSchema.parse(locale);
183
+ }
184
+ function getTranslations(locale) {
185
+ return createLocaleTranslator(languages, getLocale(locale), fallbackLocale);
186
+ }
187
+ function getFormat(locale) {
188
+ return createFormat({
189
+ locale: getLocale(locale),
190
+ fallbackLocale,
191
+ signatureLocale,
192
+ languages,
193
+ timeZone
194
+ });
195
+ }
196
+ function getCurrentLanguage(locale) {
197
+ return getLanguage(languages, getLocale(locale), fallbackLocale);
198
+ }
199
+ return {
200
+ languages,
201
+ fallbackLocale,
202
+ signatureLocale,
203
+ localeState,
204
+ setLocale,
205
+ getTranslations,
206
+ getFormat,
207
+ getLanguage: getCurrentLanguage
208
+ };
209
+ }
210
+ export {
211
+ createFormatForLocale,
212
+ createI18n,
213
+ createLocaleFormatter,
214
+ createLocaleTranslator,
215
+ defineLanguages,
216
+ getLanguage
217
+ };
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@sveltebase/i18n",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "main": "./dist/index.js",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
18
+ }
19
+ },
20
+ "dependencies": {
21
+ "@sveltebase/state": "0.1.0",
22
+ "date-fns": "^4.1.0",
23
+ "use-intl": "^4.4.0",
24
+ "zod": "^4.1.11"
25
+ },
26
+ "peerDependencies": {
27
+ "svelte": "^5.0.0"
28
+ },
29
+ "scripts": {
30
+ "build": "tsup src/index.ts --format esm --dts --clean",
31
+ "check": "tsc --project tsconfig.json --noEmit",
32
+ "lint": "bun run build && publint --strict --pack npm",
33
+ "clean": "rm -rf dist"
34
+ }
35
+ }