@to-kn/koa-locales 2.0.1 → 2.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.
- package/dist/cjs/index.js +250 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +234 -260
- package/package.json +5 -3
@@ -0,0 +1,250 @@
|
|
1
|
+
import fs from "node:fs";
|
2
|
+
import { createRequire } from "node:module";
|
3
|
+
import path from "node:path";
|
4
|
+
import util from "node:util";
|
5
|
+
import Debug from "debug";
|
6
|
+
import { ms } from "humanize-ms";
|
7
|
+
import ini from "ini";
|
8
|
+
import yaml from "js-yaml";
|
9
|
+
const DEFAULT_OPTIONS = {
|
10
|
+
defaultLocale: "en-US",
|
11
|
+
queryField: "locale",
|
12
|
+
cookieField: "locale",
|
13
|
+
localeAlias: {},
|
14
|
+
writeCookie: true,
|
15
|
+
cookieMaxAge: "1y",
|
16
|
+
dir: undefined,
|
17
|
+
dirs: [path.join(process.cwd(), "locales")],
|
18
|
+
functionName: "__",
|
19
|
+
};
|
20
|
+
function locales(app, options = {}) {
|
21
|
+
options = Object.assign({}, DEFAULT_OPTIONS, options);
|
22
|
+
const defaultLocale = formatLocale(options.defaultLocale || "en-US");
|
23
|
+
const queryField = options.queryField || "locale";
|
24
|
+
const cookieField = options.cookieField || "locale";
|
25
|
+
const cookieDomain = options.cookieDomain;
|
26
|
+
const localeAlias = options.localeAlias || {};
|
27
|
+
const writeCookie = options.writeCookie !== false;
|
28
|
+
const cookieMaxAge = ms(options.cookieMaxAge || "1y");
|
29
|
+
const localeDir = options.dir;
|
30
|
+
const localeDirs = options.dirs || [path.join(process.cwd(), "locales")];
|
31
|
+
const functionName = options.functionName || "__";
|
32
|
+
const resources = {};
|
33
|
+
/**
|
34
|
+
* @Deprecated Use options.dirs instead.
|
35
|
+
*/
|
36
|
+
if (localeDir && !localeDirs.includes(localeDir)) {
|
37
|
+
localeDirs.push(localeDir);
|
38
|
+
}
|
39
|
+
// Loop through all directories, merging resources for the same locale
|
40
|
+
// Later directories override earlier ones
|
41
|
+
for (let i = 0; i < localeDirs.length; i++) {
|
42
|
+
const dir = localeDirs[i];
|
43
|
+
if (!fs.existsSync(dir)) {
|
44
|
+
continue;
|
45
|
+
}
|
46
|
+
const names = fs.readdirSync(dir);
|
47
|
+
for (let j = 0; j < names.length; j++) {
|
48
|
+
const name = names[j];
|
49
|
+
const filepath = path.join(dir, name);
|
50
|
+
// support en_US.js => en-US.js
|
51
|
+
const locale = formatLocale(name.split(".")[0]);
|
52
|
+
let resource = {};
|
53
|
+
if (name.endsWith(".js") || name.endsWith(".cjs")) {
|
54
|
+
const require = createRequire(import.meta.url);
|
55
|
+
const mod = require(filepath);
|
56
|
+
resource = flattening((mod.default || mod));
|
57
|
+
}
|
58
|
+
else if (name.endsWith(".json")) {
|
59
|
+
const require = createRequire(import.meta.url);
|
60
|
+
resource = flattening(require(filepath));
|
61
|
+
}
|
62
|
+
else if (name.endsWith(".properties")) {
|
63
|
+
resource = ini.parse(fs.readFileSync(filepath, "utf8"));
|
64
|
+
}
|
65
|
+
else if (name.endsWith(".yml") || name.endsWith(".yaml")) {
|
66
|
+
resource = flattening(yaml.load(fs.readFileSync(filepath, "utf8")));
|
67
|
+
}
|
68
|
+
// Always merge, but let later dirs override earlier ones
|
69
|
+
resources[locale] = { ...resources[locale], ...resource };
|
70
|
+
}
|
71
|
+
}
|
72
|
+
const debug = Debug("koa-locales");
|
73
|
+
const debugSilly = Debug("koa-locales:silly");
|
74
|
+
debug("Init locales with %j, got %j resources", options, Object.keys(resources));
|
75
|
+
if (typeof app[functionName] !==
|
76
|
+
"undefined") {
|
77
|
+
console.warn('[koa-locales] will override exists "%s" function on app', functionName);
|
78
|
+
}
|
79
|
+
function gettext(locale, key, ...args) {
|
80
|
+
if (!key || key === "")
|
81
|
+
return "";
|
82
|
+
const resource = resources[locale] || {};
|
83
|
+
let text = resource[key];
|
84
|
+
if (typeof text !== "string") {
|
85
|
+
text = key;
|
86
|
+
}
|
87
|
+
debugSilly("%s: %j => %j", locale, key, text);
|
88
|
+
if (args.length === 0) {
|
89
|
+
// __(locale, key)
|
90
|
+
return text;
|
91
|
+
}
|
92
|
+
if (args.length === 1) {
|
93
|
+
const value = args[0];
|
94
|
+
if (isObject(value)) {
|
95
|
+
return formatWithObject(text, value);
|
96
|
+
}
|
97
|
+
if (Array.isArray(value)) {
|
98
|
+
return formatWithArray(text, value);
|
99
|
+
}
|
100
|
+
return util.format(text, value);
|
101
|
+
}
|
102
|
+
// __(locale, key, value1, ...)
|
103
|
+
return util.format(text, ...args);
|
104
|
+
}
|
105
|
+
// Attach to app and context using proper Koa extension
|
106
|
+
app[functionName] = gettext;
|
107
|
+
app.context[functionName] =
|
108
|
+
function (key, ...args) {
|
109
|
+
const ctx = this;
|
110
|
+
const locale = ctx.__getLocale ? ctx.__getLocale() : "";
|
111
|
+
return gettext(locale, key, ...args);
|
112
|
+
};
|
113
|
+
app.context.__getLocale =
|
114
|
+
function () {
|
115
|
+
const ctx = this;
|
116
|
+
if (typeof ctx.__locale === "string" && ctx.__locale) {
|
117
|
+
return ctx.__locale;
|
118
|
+
}
|
119
|
+
const cookieLocale = ctx.cookies.get(cookieField, { signed: false });
|
120
|
+
let locale = ctx.query[queryField];
|
121
|
+
let localeOrigin = "query";
|
122
|
+
if (!locale) {
|
123
|
+
locale = cookieLocale;
|
124
|
+
localeOrigin = "cookie";
|
125
|
+
}
|
126
|
+
if (!locale) {
|
127
|
+
let languages = ctx.acceptsLanguages();
|
128
|
+
if (languages) {
|
129
|
+
if (Array.isArray(languages)) {
|
130
|
+
if (languages[0] === "*") {
|
131
|
+
languages = languages.slice(1);
|
132
|
+
}
|
133
|
+
if (languages.length > 0) {
|
134
|
+
for (let i = 0; i < languages.length; i++) {
|
135
|
+
const lang = formatLocale(String(languages[i]));
|
136
|
+
if (resources[lang] || localeAlias[lang]) {
|
137
|
+
locale = lang;
|
138
|
+
localeOrigin = "header";
|
139
|
+
break;
|
140
|
+
}
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
else if (typeof languages === "string") {
|
145
|
+
locale = languages;
|
146
|
+
localeOrigin = "header";
|
147
|
+
}
|
148
|
+
}
|
149
|
+
if (!locale) {
|
150
|
+
locale = defaultLocale;
|
151
|
+
localeOrigin = "default";
|
152
|
+
}
|
153
|
+
}
|
154
|
+
if (locale && typeof locale !== "string") {
|
155
|
+
locale = String(locale);
|
156
|
+
}
|
157
|
+
if (locale && locale in localeAlias) {
|
158
|
+
const originalLocale = locale;
|
159
|
+
locale = localeAlias[locale];
|
160
|
+
debugSilly("Used alias, received %s but using %s", originalLocale, locale);
|
161
|
+
}
|
162
|
+
locale = formatLocale(locale || defaultLocale);
|
163
|
+
if (!resources[locale]) {
|
164
|
+
debugSilly("Locale %s is not supported. Using default (%s)", locale, defaultLocale);
|
165
|
+
locale = defaultLocale;
|
166
|
+
}
|
167
|
+
if (writeCookie && cookieLocale !== locale && !ctx.headerSent) {
|
168
|
+
updateCookie(ctx, locale);
|
169
|
+
}
|
170
|
+
debug("Locale: %s from %s", locale, localeOrigin);
|
171
|
+
debugSilly("Locale: %s from %s", locale, localeOrigin);
|
172
|
+
ctx.__locale = locale;
|
173
|
+
ctx.__localeOrigin = localeOrigin;
|
174
|
+
return String(locale);
|
175
|
+
};
|
176
|
+
app.context.__getLocaleOrigin =
|
177
|
+
function () {
|
178
|
+
const ctx = this;
|
179
|
+
if (typeof ctx.__localeOrigin === "string" && ctx.__localeOrigin)
|
180
|
+
return ctx.__localeOrigin;
|
181
|
+
ctx.__getLocale?.();
|
182
|
+
return String(ctx.__localeOrigin ?? "");
|
183
|
+
};
|
184
|
+
app.context.__setLocale =
|
185
|
+
function (locale) {
|
186
|
+
const ctx = this;
|
187
|
+
ctx.__locale = locale;
|
188
|
+
ctx.__localeOrigin = "set";
|
189
|
+
updateCookie(ctx, locale);
|
190
|
+
};
|
191
|
+
function updateCookie(ctx, locale) {
|
192
|
+
const cookieOptions = {
|
193
|
+
httpOnly: false,
|
194
|
+
maxAge: cookieMaxAge,
|
195
|
+
signed: false,
|
196
|
+
domain: cookieDomain,
|
197
|
+
overwrite: true,
|
198
|
+
};
|
199
|
+
ctx.cookies.set(cookieField, locale, cookieOptions);
|
200
|
+
debugSilly("Saved cookie with locale %s", locale);
|
201
|
+
}
|
202
|
+
}
|
203
|
+
function isObject(obj) {
|
204
|
+
return Object.prototype.toString.call(obj) === "[object Object]";
|
205
|
+
}
|
206
|
+
const ARRAY_INDEX_RE = /\{(\d+)\}/g;
|
207
|
+
function formatWithArray(text, values) {
|
208
|
+
return text.replace(ARRAY_INDEX_RE, (orignal, matched) => {
|
209
|
+
const index = parseInt(matched);
|
210
|
+
if (index < values.length) {
|
211
|
+
return String(values[index]);
|
212
|
+
}
|
213
|
+
// not match index, return orignal text
|
214
|
+
return orignal;
|
215
|
+
});
|
216
|
+
}
|
217
|
+
const Object_INDEX_RE = /\{(.+?)\}/g;
|
218
|
+
function formatWithObject(text, values) {
|
219
|
+
return text.replace(Object_INDEX_RE, (orignal, matched) => {
|
220
|
+
const value = values[matched];
|
221
|
+
if (value !== undefined && value !== null) {
|
222
|
+
return String(value);
|
223
|
+
}
|
224
|
+
// not match index, return orignal text
|
225
|
+
return orignal;
|
226
|
+
});
|
227
|
+
}
|
228
|
+
function formatLocale(locale) {
|
229
|
+
if (!locale)
|
230
|
+
return "";
|
231
|
+
return locale.replace(/_/g, "-").toLowerCase();
|
232
|
+
}
|
233
|
+
function flattening(data) {
|
234
|
+
const result = {};
|
235
|
+
function deepFlat(data, keys) {
|
236
|
+
Object.keys(data).forEach((key) => {
|
237
|
+
const value = data[key];
|
238
|
+
const k = keys ? `${keys}.${key}` : key;
|
239
|
+
if (isObject(value)) {
|
240
|
+
deepFlat(value, k);
|
241
|
+
}
|
242
|
+
else {
|
243
|
+
result[k] = String(value);
|
244
|
+
}
|
245
|
+
});
|
246
|
+
}
|
247
|
+
deepFlat(data, "");
|
248
|
+
return result;
|
249
|
+
}
|
250
|
+
export default locales;
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
import type Koa from "koa";
|
2
|
+
export interface LocalesOptions {
|
3
|
+
defaultLocale?: string;
|
4
|
+
queryField?: string;
|
5
|
+
cookieField?: string;
|
6
|
+
cookieDomain?: string;
|
7
|
+
localeAlias?: Record<string, string>;
|
8
|
+
writeCookie?: boolean;
|
9
|
+
cookieMaxAge?: string | number;
|
10
|
+
dir?: string;
|
11
|
+
dirs?: string[];
|
12
|
+
functionName?: string;
|
13
|
+
}
|
14
|
+
type GettextFunction = (locale: string, key: string, ...args: unknown[]) => string;
|
15
|
+
declare module "koa" {
|
16
|
+
interface Context {
|
17
|
+
__: GettextFunction;
|
18
|
+
__getLocale(): string;
|
19
|
+
__setLocale(locale: string): void;
|
20
|
+
__getLocaleOrigin(): string;
|
21
|
+
}
|
22
|
+
interface Application {
|
23
|
+
__: GettextFunction;
|
24
|
+
}
|
25
|
+
}
|
26
|
+
declare function locales(app: Koa, options?: LocalesOptions): void;
|
27
|
+
export default locales;
|
package/dist/index.js
CHANGED
@@ -1,276 +1,250 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
const assign = require("object-assign");
|
10
|
-
const yaml = require("js-yaml");
|
1
|
+
import fs from "node:fs";
|
2
|
+
import { createRequire } from "node:module";
|
3
|
+
import path from "node:path";
|
4
|
+
import util from "node:util";
|
5
|
+
import Debug from "debug";
|
6
|
+
import { ms } from "humanize-ms";
|
7
|
+
import ini from "ini";
|
8
|
+
import yaml from "js-yaml";
|
11
9
|
const DEFAULT_OPTIONS = {
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
10
|
+
defaultLocale: "en-US",
|
11
|
+
queryField: "locale",
|
12
|
+
cookieField: "locale",
|
13
|
+
localeAlias: {},
|
14
|
+
writeCookie: true,
|
15
|
+
cookieMaxAge: "1y",
|
16
|
+
dir: undefined,
|
17
|
+
dirs: [path.join(process.cwd(), "locales")],
|
18
|
+
functionName: "__",
|
21
19
|
};
|
22
20
|
function locales(app, options = {}) {
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
debugSilly("Locale: %s from %s", locale, localeOrigin);
|
205
|
-
this.__locale = locale;
|
206
|
-
this.__localeOrigin = localeOrigin;
|
207
|
-
return locale;
|
208
|
-
};
|
209
|
-
app.context.__getLocaleOrigin = function () {
|
210
|
-
if (this.__localeOrigin) return this.__localeOrigin;
|
211
|
-
this.__getLocale();
|
212
|
-
return this.__localeOrigin;
|
213
|
-
};
|
214
|
-
app.context.__setLocale = function (locale) {
|
215
|
-
this.__locale = locale;
|
216
|
-
this.__localeOrigin = "set";
|
217
|
-
updateCookie(this, locale);
|
218
|
-
};
|
219
|
-
function updateCookie(ctx, locale) {
|
220
|
-
const cookieOptions = {
|
221
|
-
httpOnly: false,
|
222
|
-
maxAge: cookieMaxAge,
|
223
|
-
signed: false,
|
224
|
-
domain: cookieDomain,
|
225
|
-
overwrite: true,
|
226
|
-
};
|
227
|
-
ctx.cookies.set(cookieField, locale, cookieOptions);
|
228
|
-
debugSilly("Saved cookie with locale %s", locale);
|
229
|
-
}
|
21
|
+
options = Object.assign({}, DEFAULT_OPTIONS, options);
|
22
|
+
const defaultLocale = formatLocale(options.defaultLocale || "en-US");
|
23
|
+
const queryField = options.queryField || "locale";
|
24
|
+
const cookieField = options.cookieField || "locale";
|
25
|
+
const cookieDomain = options.cookieDomain;
|
26
|
+
const localeAlias = options.localeAlias || {};
|
27
|
+
const writeCookie = options.writeCookie !== false;
|
28
|
+
const cookieMaxAge = ms(options.cookieMaxAge || "1y");
|
29
|
+
const localeDir = options.dir;
|
30
|
+
const localeDirs = options.dirs || [path.join(process.cwd(), "locales")];
|
31
|
+
const functionName = options.functionName || "__";
|
32
|
+
const resources = {};
|
33
|
+
/**
|
34
|
+
* @Deprecated Use options.dirs instead.
|
35
|
+
*/
|
36
|
+
if (localeDir && !localeDirs.includes(localeDir)) {
|
37
|
+
localeDirs.push(localeDir);
|
38
|
+
}
|
39
|
+
// Loop through all directories, merging resources for the same locale
|
40
|
+
// Later directories override earlier ones
|
41
|
+
for (let i = 0; i < localeDirs.length; i++) {
|
42
|
+
const dir = localeDirs[i];
|
43
|
+
if (!fs.existsSync(dir)) {
|
44
|
+
continue;
|
45
|
+
}
|
46
|
+
const names = fs.readdirSync(dir);
|
47
|
+
for (let j = 0; j < names.length; j++) {
|
48
|
+
const name = names[j];
|
49
|
+
const filepath = path.join(dir, name);
|
50
|
+
// support en_US.js => en-US.js
|
51
|
+
const locale = formatLocale(name.split(".")[0]);
|
52
|
+
let resource = {};
|
53
|
+
if (name.endsWith(".js") || name.endsWith(".cjs")) {
|
54
|
+
const require = createRequire(import.meta.url);
|
55
|
+
const mod = require(filepath);
|
56
|
+
resource = flattening((mod.default || mod));
|
57
|
+
}
|
58
|
+
else if (name.endsWith(".json")) {
|
59
|
+
const require = createRequire(import.meta.url);
|
60
|
+
resource = flattening(require(filepath));
|
61
|
+
}
|
62
|
+
else if (name.endsWith(".properties")) {
|
63
|
+
resource = ini.parse(fs.readFileSync(filepath, "utf8"));
|
64
|
+
}
|
65
|
+
else if (name.endsWith(".yml") || name.endsWith(".yaml")) {
|
66
|
+
resource = flattening(yaml.load(fs.readFileSync(filepath, "utf8")));
|
67
|
+
}
|
68
|
+
// Always merge, but let later dirs override earlier ones
|
69
|
+
resources[locale] = { ...resources[locale], ...resource };
|
70
|
+
}
|
71
|
+
}
|
72
|
+
const debug = Debug("koa-locales");
|
73
|
+
const debugSilly = Debug("koa-locales:silly");
|
74
|
+
debug("Init locales with %j, got %j resources", options, Object.keys(resources));
|
75
|
+
if (typeof app[functionName] !==
|
76
|
+
"undefined") {
|
77
|
+
console.warn('[koa-locales] will override exists "%s" function on app', functionName);
|
78
|
+
}
|
79
|
+
function gettext(locale, key, ...args) {
|
80
|
+
if (!key || key === "")
|
81
|
+
return "";
|
82
|
+
const resource = resources[locale] || {};
|
83
|
+
let text = resource[key];
|
84
|
+
if (typeof text !== "string") {
|
85
|
+
text = key;
|
86
|
+
}
|
87
|
+
debugSilly("%s: %j => %j", locale, key, text);
|
88
|
+
if (args.length === 0) {
|
89
|
+
// __(locale, key)
|
90
|
+
return text;
|
91
|
+
}
|
92
|
+
if (args.length === 1) {
|
93
|
+
const value = args[0];
|
94
|
+
if (isObject(value)) {
|
95
|
+
return formatWithObject(text, value);
|
96
|
+
}
|
97
|
+
if (Array.isArray(value)) {
|
98
|
+
return formatWithArray(text, value);
|
99
|
+
}
|
100
|
+
return util.format(text, value);
|
101
|
+
}
|
102
|
+
// __(locale, key, value1, ...)
|
103
|
+
return util.format(text, ...args);
|
104
|
+
}
|
105
|
+
// Attach to app and context using proper Koa extension
|
106
|
+
app[functionName] = gettext;
|
107
|
+
app.context[functionName] =
|
108
|
+
function (key, ...args) {
|
109
|
+
const ctx = this;
|
110
|
+
const locale = ctx.__getLocale ? ctx.__getLocale() : "";
|
111
|
+
return gettext(locale, key, ...args);
|
112
|
+
};
|
113
|
+
app.context.__getLocale =
|
114
|
+
function () {
|
115
|
+
const ctx = this;
|
116
|
+
if (typeof ctx.__locale === "string" && ctx.__locale) {
|
117
|
+
return ctx.__locale;
|
118
|
+
}
|
119
|
+
const cookieLocale = ctx.cookies.get(cookieField, { signed: false });
|
120
|
+
let locale = ctx.query[queryField];
|
121
|
+
let localeOrigin = "query";
|
122
|
+
if (!locale) {
|
123
|
+
locale = cookieLocale;
|
124
|
+
localeOrigin = "cookie";
|
125
|
+
}
|
126
|
+
if (!locale) {
|
127
|
+
let languages = ctx.acceptsLanguages();
|
128
|
+
if (languages) {
|
129
|
+
if (Array.isArray(languages)) {
|
130
|
+
if (languages[0] === "*") {
|
131
|
+
languages = languages.slice(1);
|
132
|
+
}
|
133
|
+
if (languages.length > 0) {
|
134
|
+
for (let i = 0; i < languages.length; i++) {
|
135
|
+
const lang = formatLocale(String(languages[i]));
|
136
|
+
if (resources[lang] || localeAlias[lang]) {
|
137
|
+
locale = lang;
|
138
|
+
localeOrigin = "header";
|
139
|
+
break;
|
140
|
+
}
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
else if (typeof languages === "string") {
|
145
|
+
locale = languages;
|
146
|
+
localeOrigin = "header";
|
147
|
+
}
|
148
|
+
}
|
149
|
+
if (!locale) {
|
150
|
+
locale = defaultLocale;
|
151
|
+
localeOrigin = "default";
|
152
|
+
}
|
153
|
+
}
|
154
|
+
if (locale && typeof locale !== "string") {
|
155
|
+
locale = String(locale);
|
156
|
+
}
|
157
|
+
if (locale && locale in localeAlias) {
|
158
|
+
const originalLocale = locale;
|
159
|
+
locale = localeAlias[locale];
|
160
|
+
debugSilly("Used alias, received %s but using %s", originalLocale, locale);
|
161
|
+
}
|
162
|
+
locale = formatLocale(locale || defaultLocale);
|
163
|
+
if (!resources[locale]) {
|
164
|
+
debugSilly("Locale %s is not supported. Using default (%s)", locale, defaultLocale);
|
165
|
+
locale = defaultLocale;
|
166
|
+
}
|
167
|
+
if (writeCookie && cookieLocale !== locale && !ctx.headerSent) {
|
168
|
+
updateCookie(ctx, locale);
|
169
|
+
}
|
170
|
+
debug("Locale: %s from %s", locale, localeOrigin);
|
171
|
+
debugSilly("Locale: %s from %s", locale, localeOrigin);
|
172
|
+
ctx.__locale = locale;
|
173
|
+
ctx.__localeOrigin = localeOrigin;
|
174
|
+
return String(locale);
|
175
|
+
};
|
176
|
+
app.context.__getLocaleOrigin =
|
177
|
+
function () {
|
178
|
+
const ctx = this;
|
179
|
+
if (typeof ctx.__localeOrigin === "string" && ctx.__localeOrigin)
|
180
|
+
return ctx.__localeOrigin;
|
181
|
+
ctx.__getLocale?.();
|
182
|
+
return String(ctx.__localeOrigin ?? "");
|
183
|
+
};
|
184
|
+
app.context.__setLocale =
|
185
|
+
function (locale) {
|
186
|
+
const ctx = this;
|
187
|
+
ctx.__locale = locale;
|
188
|
+
ctx.__localeOrigin = "set";
|
189
|
+
updateCookie(ctx, locale);
|
190
|
+
};
|
191
|
+
function updateCookie(ctx, locale) {
|
192
|
+
const cookieOptions = {
|
193
|
+
httpOnly: false,
|
194
|
+
maxAge: cookieMaxAge,
|
195
|
+
signed: false,
|
196
|
+
domain: cookieDomain,
|
197
|
+
overwrite: true,
|
198
|
+
};
|
199
|
+
ctx.cookies.set(cookieField, locale, cookieOptions);
|
200
|
+
debugSilly("Saved cookie with locale %s", locale);
|
201
|
+
}
|
230
202
|
}
|
231
203
|
function isObject(obj) {
|
232
|
-
|
204
|
+
return Object.prototype.toString.call(obj) === "[object Object]";
|
233
205
|
}
|
234
206
|
const ARRAY_INDEX_RE = /\{(\d+)\}/g;
|
235
207
|
function formatWithArray(text, values) {
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
208
|
+
return text.replace(ARRAY_INDEX_RE, (orignal, matched) => {
|
209
|
+
const index = parseInt(matched);
|
210
|
+
if (index < values.length) {
|
211
|
+
return String(values[index]);
|
212
|
+
}
|
213
|
+
// not match index, return orignal text
|
214
|
+
return orignal;
|
215
|
+
});
|
244
216
|
}
|
245
217
|
const Object_INDEX_RE = /\{(.+?)\}/g;
|
246
218
|
function formatWithObject(text, values) {
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
219
|
+
return text.replace(Object_INDEX_RE, (orignal, matched) => {
|
220
|
+
const value = values[matched];
|
221
|
+
if (value !== undefined && value !== null) {
|
222
|
+
return String(value);
|
223
|
+
}
|
224
|
+
// not match index, return orignal text
|
225
|
+
return orignal;
|
226
|
+
});
|
255
227
|
}
|
256
228
|
function formatLocale(locale) {
|
257
|
-
|
258
|
-
|
229
|
+
if (!locale)
|
230
|
+
return "";
|
231
|
+
return locale.replace(/_/g, "-").toLowerCase();
|
259
232
|
}
|
260
233
|
function flattening(data) {
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
234
|
+
const result = {};
|
235
|
+
function deepFlat(data, keys) {
|
236
|
+
Object.keys(data).forEach((key) => {
|
237
|
+
const value = data[key];
|
238
|
+
const k = keys ? `${keys}.${key}` : key;
|
239
|
+
if (isObject(value)) {
|
240
|
+
deepFlat(value, k);
|
241
|
+
}
|
242
|
+
else {
|
243
|
+
result[k] = String(value);
|
244
|
+
}
|
245
|
+
});
|
246
|
+
}
|
247
|
+
deepFlat(data, "");
|
248
|
+
return result;
|
275
249
|
}
|
276
|
-
|
250
|
+
export default locales;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@to-kn/koa-locales",
|
3
|
-
"version": "2.0
|
3
|
+
"version": "2.1.0",
|
4
4
|
"description": "koa locales, i18n solution for koa",
|
5
5
|
"keywords": [
|
6
6
|
"koa-locales",
|
@@ -26,14 +26,16 @@
|
|
26
26
|
"require": "./dist/cjs/index.cjs",
|
27
27
|
"types": "./dist/types/index.d.ts"
|
28
28
|
},
|
29
|
-
"main": "./dist/
|
29
|
+
"main": "./dist/cjs/index.cjs",
|
30
30
|
"module": "./dist/esm/index.js",
|
31
31
|
"types": "./dist/types/index.d.ts",
|
32
32
|
"files": [
|
33
33
|
"dist/"
|
34
34
|
],
|
35
35
|
"scripts": {
|
36
|
-
"build": "tsc",
|
36
|
+
"build:esm": "tsc --project tsconfig.json",
|
37
|
+
"build:cjs": "tsc --project tsconfig.cjs.json",
|
38
|
+
"build": "npm run build:esm && npm run build:cjs",
|
37
39
|
"ci": "npm run lint && npm run cov",
|
38
40
|
"contributors": "contributors -f plain -o AUTHORS",
|
39
41
|
"cov": "vitest run --coverage",
|