@voilabs/oilang 0.0.4 → 0.0.5
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/README.md +45 -13
- package/dist/adapters/PostgreSQL.js +116 -98
- package/dist/oilang.js +159 -49
- package/dist/types.js +0 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -43,16 +43,17 @@ async function main() {
|
|
|
43
43
|
await oilang.init();
|
|
44
44
|
|
|
45
45
|
// Create a new locale
|
|
46
|
-
await oilang.
|
|
46
|
+
await oilang.locales.create("en-US", "English (US)", "English");
|
|
47
47
|
|
|
48
48
|
// Add a translation key
|
|
49
|
-
await oilang.
|
|
49
|
+
await oilang.translations.create("en-US", {
|
|
50
50
|
key: "greeting",
|
|
51
51
|
value: "Hello, World!",
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
// Retrieve translations
|
|
55
|
-
|
|
55
|
+
// Example: Translations are stored in memory or Redis as a key-value pair.
|
|
56
|
+
const translations = await oilang.translations.list("en-US");
|
|
56
57
|
console.log(translations);
|
|
57
58
|
}
|
|
58
59
|
|
|
@@ -63,7 +64,7 @@ main();
|
|
|
63
64
|
|
|
64
65
|
### OILang
|
|
65
66
|
|
|
66
|
-
The main entry point for the library. It orchestrates the interaction between the database adapter and the store.
|
|
67
|
+
The main entry point for the library. It orchestrates the interaction between the database adapter and the store, delegating specific operations to `locales` and `translations` namespaces.
|
|
67
68
|
|
|
68
69
|
#### Constructor
|
|
69
70
|
|
|
@@ -78,21 +79,50 @@ new OILang(config: AdapterConfig)
|
|
|
78
79
|
- **`init(): Promise<void>`**
|
|
79
80
|
Connects to the database and initializes the store by loading existing locales and translations.
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
#### Namespaces
|
|
83
|
+
|
|
84
|
+
- **`oilang.locales`**: Manages locale operations.
|
|
85
|
+
- **`oilang.translations`**: Manages translation operations.
|
|
86
|
+
|
|
87
|
+
### Locales
|
|
88
|
+
|
|
89
|
+
Access via `oilang.locales`.
|
|
90
|
+
|
|
91
|
+
#### Methods
|
|
92
|
+
|
|
93
|
+
- **`list(): Promise<ActionResponse<LocaleData[]>>`**
|
|
94
|
+
Retrieves all available locales from the store.
|
|
95
|
+
|
|
96
|
+
- **`create(locale: string, nativeName: string, englishName: string): Promise<ActionResponse<LocaleData>>`**
|
|
82
97
|
Creates a new locale in the database and updates the store.
|
|
83
98
|
|
|
84
|
-
- **`
|
|
99
|
+
- **`delete(locale: string): Promise<ActionResponse<LocaleData>>`**
|
|
85
100
|
Deletes a locale and its associated translations from both the database and the store.
|
|
86
101
|
|
|
87
|
-
- **`
|
|
88
|
-
|
|
102
|
+
- **`update(locale: string, nativeName: string, englishName: string): Promise<ActionResponse<LocaleData>>`**
|
|
103
|
+
Updates locale information in the database and store.
|
|
89
104
|
|
|
90
|
-
|
|
91
|
-
|
|
105
|
+
### Translations
|
|
106
|
+
|
|
107
|
+
Access via `oilang.translations`.
|
|
92
108
|
|
|
93
|
-
|
|
109
|
+
#### Methods
|
|
110
|
+
|
|
111
|
+
- **`list(locale: string): Promise<Record<string, string>>`**
|
|
94
112
|
Retrieves all translations for a specific locale from the store. Returns a key-value map.
|
|
95
113
|
|
|
114
|
+
- **`create(locale: string, config: { key: string; value: string }): Promise<ActionResponse<TranslationData>>`**
|
|
115
|
+
Adds a translation key-value pair for a specific locale.
|
|
116
|
+
|
|
117
|
+
- **`update(locale: string, key: string, newValue: string): Promise<ActionResponse<TranslationData>>`**
|
|
118
|
+
Updates an existing translation value.
|
|
119
|
+
|
|
120
|
+
- **`delete(locale: string, key: string): Promise<ActionResponse<TranslationData>>`**
|
|
121
|
+
Deletes a translation key.
|
|
122
|
+
|
|
123
|
+
- **`translate(locale: string, key: string, variables?: Record<string, string | number>): Promise<string>`**
|
|
124
|
+
Retrieves a single translation, optionally performing variable interpolation. Uses fallback locale if translation is missing.
|
|
125
|
+
|
|
96
126
|
### Adapters
|
|
97
127
|
|
|
98
128
|
#### PostgreSQL
|
|
@@ -124,7 +154,7 @@ new MemoryStore();
|
|
|
124
154
|
|
|
125
155
|
#### RedisStore
|
|
126
156
|
|
|
127
|
-
Stores data in a Redis instance.
|
|
157
|
+
Stores data in a Redis instance. Essential for distributed applications or when data persistence across restarts (without DB reload) is desired.
|
|
128
158
|
|
|
129
159
|
**Constructor**
|
|
130
160
|
|
|
@@ -163,5 +193,7 @@ Stores the translation strings.
|
|
|
163
193
|
Most methods return a result object pattern to handle errors gracefully without throwing.
|
|
164
194
|
|
|
165
195
|
```typescript
|
|
166
|
-
type
|
|
196
|
+
type ActionResponse<T> =
|
|
197
|
+
| { error: Error & { code?: string }; data: null }
|
|
198
|
+
| { error: null; data: T };
|
|
167
199
|
```
|
|
@@ -1,136 +1,154 @@
|
|
|
1
1
|
import { Client } from "pg";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
class Locales {
|
|
3
|
+
schema;
|
|
4
4
|
client;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
this.
|
|
8
|
-
this.client = new Client(config);
|
|
9
|
-
this.schemaNames = customizationConfig.schemaNames ?? {
|
|
10
|
-
keys: "keys",
|
|
11
|
-
locales: "locales",
|
|
12
|
-
};
|
|
5
|
+
constructor(schema, client) {
|
|
6
|
+
this.schema = schema;
|
|
7
|
+
this.client = client;
|
|
13
8
|
}
|
|
14
|
-
async
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
native_name VARCHAR(255) NOT NULL,
|
|
23
|
-
english_name VARCHAR(255) NOT NULL
|
|
24
|
-
);
|
|
25
|
-
`,
|
|
26
|
-
`
|
|
27
|
-
CREATE TABLE IF NOT EXISTS ${this.schemaNames.keys} (
|
|
28
|
-
key VARCHAR(255) NOT NULL,
|
|
29
|
-
value TEXT NOT NULL,
|
|
30
|
-
locale_id VARCHAR(10) NOT NULL,
|
|
31
|
-
FOREIGN KEY (locale_id) REFERENCES ${this.schemaNames.locales}(code)
|
|
32
|
-
);
|
|
33
|
-
`,
|
|
34
|
-
];
|
|
35
|
-
await this.client.query(QUERY.join("\n"));
|
|
36
|
-
}
|
|
37
|
-
getSchemaNames() {
|
|
38
|
-
return this.schemaNames;
|
|
9
|
+
async list() {
|
|
10
|
+
try {
|
|
11
|
+
const result = await this.client.query(`SELECT * FROM ${this.schema}`);
|
|
12
|
+
return { success: true, data: result.rows };
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
return { success: false, error };
|
|
16
|
+
}
|
|
39
17
|
}
|
|
40
|
-
async
|
|
18
|
+
async create(locale, nativeName, englishName) {
|
|
41
19
|
try {
|
|
42
|
-
const
|
|
43
|
-
if (
|
|
20
|
+
const existing = await this.client.query(`SELECT * FROM ${this.schema} WHERE code = $1`, [locale]);
|
|
21
|
+
if (existing.rows.length > 0) {
|
|
44
22
|
const error = new Error("Locale already exists");
|
|
45
23
|
error.code = "LOCALE_ALREADY_EXISTS";
|
|
46
24
|
throw error;
|
|
47
25
|
}
|
|
48
|
-
const result = await this.client.query(`INSERT INTO ${this.
|
|
49
|
-
return {
|
|
50
|
-
success: true,
|
|
51
|
-
data: result.rows.at(0),
|
|
52
|
-
};
|
|
26
|
+
const result = await this.client.query(`INSERT INTO ${this.schema} (code, native_name, english_name) VALUES ($1, $2, $3) RETURNING *`, [locale, nativeName, englishName]);
|
|
27
|
+
return { success: true, data: result.rows[0] };
|
|
53
28
|
}
|
|
54
29
|
catch (error) {
|
|
55
|
-
return {
|
|
56
|
-
success: false,
|
|
57
|
-
error: error,
|
|
58
|
-
};
|
|
30
|
+
return { success: false, error };
|
|
59
31
|
}
|
|
60
32
|
}
|
|
61
|
-
async
|
|
33
|
+
async delete(locale) {
|
|
62
34
|
try {
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
success: true,
|
|
66
|
-
data: result.rows,
|
|
67
|
-
};
|
|
35
|
+
await this.client.query(`DELETE FROM ${this.schema} WHERE code = $1`, [locale]);
|
|
36
|
+
return { success: true, data: { code: locale } };
|
|
68
37
|
}
|
|
69
38
|
catch (error) {
|
|
70
|
-
return {
|
|
71
|
-
success: false,
|
|
72
|
-
error: error,
|
|
73
|
-
};
|
|
39
|
+
return { success: false, error };
|
|
74
40
|
}
|
|
75
41
|
}
|
|
76
|
-
async
|
|
42
|
+
async update(locale, nativeName, englishName) {
|
|
77
43
|
try {
|
|
78
|
-
const result = await this.client.query(`
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
44
|
+
const result = await this.client.query(`UPDATE ${this.schema} SET native_name = $2, english_name = $3 WHERE code = $1 RETURNING *`, [locale, nativeName, englishName]);
|
|
45
|
+
if (result.rows.length === 0) {
|
|
46
|
+
const error = new Error("Locale does not exist");
|
|
47
|
+
error.code = "LOCALE_NOT_FOUND";
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
return { success: true, data: result.rows[0] };
|
|
83
51
|
}
|
|
84
52
|
catch (error) {
|
|
85
|
-
return {
|
|
86
|
-
success: false,
|
|
87
|
-
error: error,
|
|
88
|
-
};
|
|
53
|
+
return { success: false, error: error };
|
|
89
54
|
}
|
|
90
55
|
}
|
|
91
|
-
|
|
56
|
+
}
|
|
57
|
+
class Translations {
|
|
58
|
+
schema;
|
|
59
|
+
client;
|
|
60
|
+
localesSchema;
|
|
61
|
+
constructor(schema, client, localesSchema) {
|
|
62
|
+
this.schema = schema;
|
|
63
|
+
this.client = client;
|
|
64
|
+
this.localesSchema = localesSchema;
|
|
65
|
+
}
|
|
66
|
+
async list() {
|
|
92
67
|
try {
|
|
93
|
-
await this.client.query(`
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
success: true,
|
|
97
|
-
data: {
|
|
98
|
-
code: locale,
|
|
99
|
-
},
|
|
100
|
-
};
|
|
68
|
+
const result = await this.client.query(`SELECT * FROM ${this.schema}`);
|
|
69
|
+
return { success: true, data: result.rows };
|
|
101
70
|
}
|
|
102
71
|
catch (error) {
|
|
103
|
-
return {
|
|
104
|
-
success: false,
|
|
105
|
-
error: error,
|
|
106
|
-
};
|
|
72
|
+
return { success: false, error };
|
|
107
73
|
}
|
|
108
74
|
}
|
|
109
|
-
async
|
|
75
|
+
async create(key, value, locale) {
|
|
110
76
|
try {
|
|
111
|
-
const
|
|
112
|
-
if (
|
|
113
|
-
const error = new Error("Translation already exists");
|
|
114
|
-
error.code = "TRANSLATION_ALREADY_EXISTS";
|
|
115
|
-
throw error;
|
|
116
|
-
}
|
|
117
|
-
const existingLocale = await this.client.query(`SELECT * FROM ${this.schemaNames.locales} WHERE code = $1`, [locale]);
|
|
118
|
-
if (existingLocale.rows.length === 0) {
|
|
77
|
+
const localeCheck = await this.client.query(`SELECT * FROM ${this.localesSchema} WHERE code = $1`, [locale]);
|
|
78
|
+
if (localeCheck.rows.length === 0) {
|
|
119
79
|
const error = new Error("Locale does not exist");
|
|
120
80
|
error.code = "LOCALE_NOT_FOUND";
|
|
121
81
|
throw error;
|
|
122
82
|
}
|
|
123
|
-
const result = await this.client.query(`INSERT INTO ${this.
|
|
124
|
-
return {
|
|
125
|
-
success: true,
|
|
126
|
-
data: result.rows.at(0),
|
|
127
|
-
};
|
|
83
|
+
const result = await this.client.query(`INSERT INTO ${this.schema} (key, value, locale_id) VALUES ($1, $2, $3) RETURNING *`, [key, value, locale]);
|
|
84
|
+
return { success: true, data: result.rows[0] };
|
|
128
85
|
}
|
|
129
86
|
catch (error) {
|
|
130
|
-
return {
|
|
131
|
-
success: false,
|
|
132
|
-
error: error,
|
|
133
|
-
};
|
|
87
|
+
return { success: false, error: error };
|
|
134
88
|
}
|
|
135
89
|
}
|
|
90
|
+
async delete(key, locale) {
|
|
91
|
+
try {
|
|
92
|
+
await this.client.query(`DELETE FROM ${this.schema} WHERE key = $1 AND locale_id = $2`, [key, locale]);
|
|
93
|
+
return { success: true, data: { locale_id: locale, key } };
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
return { success: false, error };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async update(key, value, locale) {
|
|
100
|
+
try {
|
|
101
|
+
const result = await this.client.query(`UPDATE ${this.schema} SET value = $2 WHERE key = $1 AND locale_id = $3 RETURNING *`, [key, value, locale]);
|
|
102
|
+
if (result.rows.length === 0) {
|
|
103
|
+
const error = new Error("Translation does not exist");
|
|
104
|
+
error.code = "TRANSLATION_NOT_FOUND";
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
return { success: true, data: result.rows[0] };
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
return { success: false, error: error };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export class PostgreSQL {
|
|
115
|
+
config;
|
|
116
|
+
client;
|
|
117
|
+
schemaNames;
|
|
118
|
+
locales;
|
|
119
|
+
translations;
|
|
120
|
+
constructor(config, customizationConfig) {
|
|
121
|
+
this.config = config;
|
|
122
|
+
this.client = new Client(config);
|
|
123
|
+
this.schemaNames = customizationConfig.schemaNames ?? {
|
|
124
|
+
keys: "keys",
|
|
125
|
+
locales: "locales",
|
|
126
|
+
};
|
|
127
|
+
this.locales = new Locales(this.schemaNames.locales, this.client);
|
|
128
|
+
this.translations = new Translations(this.schemaNames.keys, this.client, this.schemaNames.locales);
|
|
129
|
+
}
|
|
130
|
+
async connect() {
|
|
131
|
+
await this.client.connect();
|
|
132
|
+
const QUERY = `
|
|
133
|
+
CREATE TABLE IF NOT EXISTS ${this.schemaNames.locales} (
|
|
134
|
+
code VARCHAR(10) PRIMARY KEY,
|
|
135
|
+
native_name VARCHAR(255) NOT NULL,
|
|
136
|
+
english_name VARCHAR(255) NOT NULL,
|
|
137
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
138
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
CREATE TABLE IF NOT EXISTS ${this.schemaNames.keys} (
|
|
142
|
+
key VARCHAR(255) NOT NULL,
|
|
143
|
+
value TEXT NOT NULL,
|
|
144
|
+
locale_id VARCHAR(10) NOT NULL,
|
|
145
|
+
PRIMARY KEY (key, locale_id),
|
|
146
|
+
FOREIGN KEY (locale_id) REFERENCES ${this.schemaNames.locales}(code) ON DELETE CASCADE
|
|
147
|
+
);
|
|
148
|
+
`;
|
|
149
|
+
await this.client.query(QUERY);
|
|
150
|
+
}
|
|
151
|
+
getSchemaNames() {
|
|
152
|
+
return this.schemaNames;
|
|
153
|
+
}
|
|
136
154
|
}
|
package/dist/oilang.js
CHANGED
|
@@ -1,43 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
config;
|
|
1
|
+
class Locale {
|
|
3
2
|
database;
|
|
4
3
|
store;
|
|
5
|
-
constructor(
|
|
6
|
-
this.
|
|
7
|
-
this.
|
|
8
|
-
this.store = config.store;
|
|
4
|
+
constructor(database, store) {
|
|
5
|
+
this.database = database;
|
|
6
|
+
this.store = store;
|
|
9
7
|
}
|
|
10
|
-
async
|
|
11
|
-
await this.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
code: l.code,
|
|
19
|
-
native_name: l.native_name,
|
|
20
|
-
english_name: l.english_name,
|
|
21
|
-
created_at: l.created_at,
|
|
22
|
-
updated_at: l.updated_at,
|
|
23
|
-
}));
|
|
24
|
-
const loadableTranslations = loadableLocales.reduce((acc, l) => {
|
|
25
|
-
acc[l.code] = translations.data
|
|
26
|
-
.filter((t) => t.locale_id === l.code)
|
|
27
|
-
.reduce((acc, t) => {
|
|
28
|
-
acc[t.key] = t.value;
|
|
29
|
-
return acc;
|
|
30
|
-
}, {});
|
|
31
|
-
return acc;
|
|
32
|
-
}, {});
|
|
33
|
-
await this.store.load(loadableLocales, loadableTranslations);
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
throw new Error("Failed to load locales or translations");
|
|
37
|
-
}
|
|
8
|
+
async list() {
|
|
9
|
+
const response = await this.store.getAll({
|
|
10
|
+
seed: "locales",
|
|
11
|
+
});
|
|
12
|
+
return {
|
|
13
|
+
error: null,
|
|
14
|
+
data: response,
|
|
15
|
+
};
|
|
38
16
|
}
|
|
39
|
-
async
|
|
40
|
-
const response = await this.database.
|
|
17
|
+
async create(locale, nativeName, englishName) {
|
|
18
|
+
const response = await this.database.locales.create(locale, nativeName, englishName);
|
|
41
19
|
if (response.success) {
|
|
42
20
|
this.store.set({
|
|
43
21
|
seed: "locales",
|
|
@@ -55,8 +33,8 @@ export class OILang {
|
|
|
55
33
|
};
|
|
56
34
|
}
|
|
57
35
|
}
|
|
58
|
-
async
|
|
59
|
-
const response = await this.database.
|
|
36
|
+
async delete(locale) {
|
|
37
|
+
const response = await this.database.locales.delete(locale);
|
|
60
38
|
if (response.success) {
|
|
61
39
|
this.store.remove({
|
|
62
40
|
seed: "locales",
|
|
@@ -74,24 +52,48 @@ export class OILang {
|
|
|
74
52
|
};
|
|
75
53
|
}
|
|
76
54
|
}
|
|
77
|
-
async
|
|
78
|
-
const response = await this.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
55
|
+
async update(locale, nativeName, englishName) {
|
|
56
|
+
const response = await this.database.locales.update(locale, nativeName, englishName);
|
|
57
|
+
if (response.success) {
|
|
58
|
+
this.store.update({
|
|
59
|
+
seed: "locales",
|
|
60
|
+
code: locale,
|
|
61
|
+
locale: {
|
|
62
|
+
native_name: nativeName,
|
|
63
|
+
english_name: englishName,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
error: null,
|
|
68
|
+
data: response.data,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
return {
|
|
73
|
+
error: response.error,
|
|
74
|
+
data: null,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
85
77
|
}
|
|
86
|
-
|
|
78
|
+
}
|
|
79
|
+
class Translation {
|
|
80
|
+
database;
|
|
81
|
+
store;
|
|
82
|
+
fallbackLocale;
|
|
83
|
+
constructor(database, store, fallbackLocale) {
|
|
84
|
+
this.database = database;
|
|
85
|
+
this.store = store;
|
|
86
|
+
this.fallbackLocale = fallbackLocale;
|
|
87
|
+
}
|
|
88
|
+
async list(locale) {
|
|
87
89
|
const response = await this.store.getAll({
|
|
88
90
|
seed: "translations",
|
|
89
91
|
locale,
|
|
90
92
|
});
|
|
91
93
|
return response;
|
|
92
94
|
}
|
|
93
|
-
async
|
|
94
|
-
const response = await this.database.
|
|
95
|
+
async create(locale, config) {
|
|
96
|
+
const response = await this.database.translations.create(config.key, config.value, locale);
|
|
95
97
|
if (response.success) {
|
|
96
98
|
this.store.set({
|
|
97
99
|
seed: "translations",
|
|
@@ -111,4 +113,112 @@ export class OILang {
|
|
|
111
113
|
};
|
|
112
114
|
}
|
|
113
115
|
}
|
|
116
|
+
async update(locale, key, newValue) {
|
|
117
|
+
const response = await this.database.translations.update(key, newValue, locale);
|
|
118
|
+
if (response.success) {
|
|
119
|
+
this.store.update({
|
|
120
|
+
seed: "translations",
|
|
121
|
+
locale,
|
|
122
|
+
key,
|
|
123
|
+
value: newValue,
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
error: null,
|
|
127
|
+
data: response.data,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
return {
|
|
132
|
+
error: response.error,
|
|
133
|
+
data: null,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
async delete(locale, key) {
|
|
138
|
+
const response = await this.database.translations.delete(key, locale);
|
|
139
|
+
if (response.success) {
|
|
140
|
+
this.store.remove({
|
|
141
|
+
seed: "translations",
|
|
142
|
+
locale,
|
|
143
|
+
key,
|
|
144
|
+
});
|
|
145
|
+
return {
|
|
146
|
+
error: null,
|
|
147
|
+
data: response.data,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
return {
|
|
152
|
+
error: response.error,
|
|
153
|
+
data: null,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async translate(locale, key, variables) {
|
|
158
|
+
let translation = await this.store.get({
|
|
159
|
+
seed: "translations",
|
|
160
|
+
locale,
|
|
161
|
+
key,
|
|
162
|
+
});
|
|
163
|
+
if (!translation && this.fallbackLocale) {
|
|
164
|
+
translation = await this.store.get({
|
|
165
|
+
seed: "translations",
|
|
166
|
+
locale: this.fallbackLocale,
|
|
167
|
+
key,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
if (!translation)
|
|
171
|
+
return key;
|
|
172
|
+
if (variables) {
|
|
173
|
+
for (const [varKey, varValue] of Object.entries(variables)) {
|
|
174
|
+
translation = translation.replace(new RegExp(`{{${varKey}}}`, "g"), String(varValue));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return translation;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export class OILang {
|
|
181
|
+
config;
|
|
182
|
+
database;
|
|
183
|
+
store;
|
|
184
|
+
fallbackLocale;
|
|
185
|
+
locales;
|
|
186
|
+
translations;
|
|
187
|
+
constructor(config) {
|
|
188
|
+
this.config = config;
|
|
189
|
+
this.database = config.database;
|
|
190
|
+
this.store = config.store;
|
|
191
|
+
this.fallbackLocale = config.fallbackLocale ?? "en-US";
|
|
192
|
+
this.locales = new Locale(this.database, this.store);
|
|
193
|
+
this.translations = new Translation(this.database, this.store, this.fallbackLocale);
|
|
194
|
+
}
|
|
195
|
+
async init() {
|
|
196
|
+
await this.database.connect();
|
|
197
|
+
const [locales, translations] = await Promise.all([
|
|
198
|
+
this.database.locales.list(),
|
|
199
|
+
this.database.translations.list(),
|
|
200
|
+
]);
|
|
201
|
+
if (locales.success && translations.success) {
|
|
202
|
+
const loadableLocales = locales.data.map((l) => ({
|
|
203
|
+
code: l.code,
|
|
204
|
+
native_name: l.native_name,
|
|
205
|
+
english_name: l.english_name,
|
|
206
|
+
created_at: l.created_at,
|
|
207
|
+
updated_at: l.updated_at,
|
|
208
|
+
}));
|
|
209
|
+
const loadableTranslations = loadableLocales.reduce((acc, l) => {
|
|
210
|
+
acc[l.code] = translations.data
|
|
211
|
+
.filter((t) => t.locale_id === l.code)
|
|
212
|
+
.reduce((accData, t) => {
|
|
213
|
+
accData[t.key] = t.value;
|
|
214
|
+
return accData;
|
|
215
|
+
}, {});
|
|
216
|
+
return acc;
|
|
217
|
+
}, {});
|
|
218
|
+
await this.store.load(loadableLocales, loadableTranslations);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
throw new Error("Failed to load locales or translations");
|
|
222
|
+
}
|
|
223
|
+
}
|
|
114
224
|
}
|
package/dist/types.js
ADDED
|
File without changes
|
package/package.json
CHANGED