@to-kn/koa-locales 2.0.2 → 2.1.1

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,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;
@@ -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
- const Debug = require("debug");
2
- const debug = Debug("koa-locales");
3
- const debugSilly = Debug("koa-locales:silly");
4
- const ini = require("ini");
5
- const util = require("util");
6
- const fs = require("fs");
7
- const path = require("path");
8
- const ms = require("humanize-ms").default || require("humanize-ms");
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
- defaultLocale: "en-US",
13
- queryField: "locale",
14
- cookieField: "locale",
15
- localeAlias: {},
16
- writeCookie: true,
17
- cookieMaxAge: "1y",
18
- dir: undefined,
19
- dirs: [path.join(process.cwd(), "locales")],
20
- functionName: "__",
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
- options = assign({}, DEFAULT_OPTIONS, options);
24
- const defaultLocale = formatLocale(options.defaultLocale || "en-US");
25
- const queryField = options.queryField || "locale";
26
- const cookieField = options.cookieField || "locale";
27
- const cookieDomain = options.cookieDomain;
28
- const localeAlias = options.localeAlias || {};
29
- const writeCookie = options.writeCookie !== false;
30
- const cookieMaxAge = ms(options.cookieMaxAge || "1y");
31
- const localeDir = options.dir;
32
- const localeDirs = options.dirs || [path.join(process.cwd(), "locales")];
33
- const functionName = options.functionName || "__";
34
- const resources = {};
35
- /**
36
- * @Deprecated Use options.dirs instead.
37
- */
38
- if (localeDir && !localeDirs.includes(localeDir)) {
39
- localeDirs.push(localeDir);
40
- }
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(".json")) {
54
- resource = flattening(require(filepath));
55
- } else if (name.endsWith(".properties")) {
56
- resource = ini.parse(fs.readFileSync(filepath, "utf8"));
57
- } else if (name.endsWith(".yml") || name.endsWith(".yaml")) {
58
- resource = flattening(yaml.safeLoad(fs.readFileSync(filepath, "utf8")));
59
- }
60
- resources[locale] = resources[locale] || {};
61
- assign(resources[locale], resource);
62
- }
63
- }
64
- debug(
65
- "Init locales with %j, got %j resources",
66
- options,
67
- Object.keys(resources),
68
- );
69
- if (typeof app[functionName] !== "undefined") {
70
- console.warn(
71
- '[koa-locales] will override exists "%s" function on app',
72
- functionName,
73
- );
74
- }
75
- function gettext(locale, key, value) {
76
- if (arguments.length === 0 || arguments.length === 1) {
77
- // __()
78
- // --('en')
79
- return "";
80
- }
81
- const resource = resources[locale] || {};
82
- let text = resource[key];
83
- if (text === undefined) {
84
- text = key;
85
- }
86
- debugSilly("%s: %j => %j", locale, key, text);
87
- if (!text) {
88
- return "";
89
- }
90
- if (arguments.length === 2) {
91
- // __(locale, key)
92
- return text;
93
- }
94
- if (arguments.length === 3) {
95
- if (isObject(value)) {
96
- // __(locale, key, object)
97
- // __('zh', '{a} {b} {b} {a}', {a: 'foo', b: 'bar'})
98
- // =>
99
- // foo bar bar foo
100
- return formatWithObject(text, value);
101
- }
102
- if (Array.isArray(value)) {
103
- // __(locale, key, array)
104
- // __('zh', '{0} {1} {1} {0}', ['foo', 'bar'])
105
- // =>
106
- // foo bar bar foo
107
- return formatWithArray(text, value);
108
- }
109
- // __(locale, key, value)
110
- return util.format(text, value);
111
- }
112
- // __(locale, key, value1, ...)
113
- const args = new Array(arguments.length - 1);
114
- args[0] = text;
115
- for (let i = 2; i < arguments.length; i++) {
116
- args[i - 1] = arguments[i];
117
- }
118
- return util.format.apply(util, args);
119
- }
120
- app[functionName] = gettext;
121
- app.context[functionName] = function (key, value) {
122
- if (arguments.length === 0) {
123
- // __()
124
- return "";
125
- }
126
- const locale = this.__getLocale();
127
- if (arguments.length === 1) {
128
- return gettext(locale, key);
129
- }
130
- if (arguments.length === 2) {
131
- return gettext(locale, key, value);
132
- }
133
- const args = new Array(arguments.length + 1);
134
- args[0] = locale;
135
- for (let i = 0; i < arguments.length; i++) {
136
- args[i + 1] = arguments[i];
137
- }
138
- // @ts-expect-error: dynamic argument forwarding
139
- return gettext(...args);
140
- };
141
- app.context.__getLocale = function () {
142
- if (this.__locale) {
143
- return this.__locale;
144
- }
145
- const cookieLocale = this.cookies.get(cookieField, { signed: false });
146
- // 1. Query
147
- let locale = this.query[queryField];
148
- let localeOrigin = "query";
149
- // 2. Cookie
150
- if (!locale) {
151
- locale = cookieLocale;
152
- localeOrigin = "cookie";
153
- }
154
- // 3. Header
155
- if (!locale) {
156
- let languages = this.acceptsLanguages();
157
- if (languages) {
158
- if (Array.isArray(languages)) {
159
- if (languages[0] === "*") {
160
- languages = languages.slice(1);
161
- }
162
- if (languages.length > 0) {
163
- for (let i = 0; i < languages.length; i++) {
164
- const lang = formatLocale(languages[i]);
165
- if (resources[lang] || localeAlias[lang]) {
166
- locale = lang;
167
- localeOrigin = "header";
168
- break;
169
- }
170
- }
171
- }
172
- } else {
173
- locale = languages;
174
- localeOrigin = "header";
175
- }
176
- }
177
- if (!locale) {
178
- locale = defaultLocale;
179
- localeOrigin = "default";
180
- }
181
- }
182
- if (locale && locale in localeAlias) {
183
- const originalLocale = locale;
184
- locale = localeAlias[locale];
185
- debugSilly(
186
- "Used alias, received %s but using %s",
187
- originalLocale,
188
- locale,
189
- );
190
- }
191
- locale = formatLocale(locale || defaultLocale);
192
- if (!resources[locale]) {
193
- debugSilly(
194
- "Locale %s is not supported. Using default (%s)",
195
- locale,
196
- defaultLocale,
197
- );
198
- locale = defaultLocale;
199
- }
200
- if (writeCookie && cookieLocale !== locale && !this.headerSent) {
201
- updateCookie(this, locale);
202
- }
203
- debug("Locale: %s from %s", locale, localeOrigin);
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
- return Object.prototype.toString.call(obj) === "[object Object]";
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
- return text.replace(ARRAY_INDEX_RE, (orignal, matched) => {
237
- const index = parseInt(matched);
238
- if (index < values.length) {
239
- return values[index];
240
- }
241
- // not match index, return orignal text
242
- return orignal;
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
- return text.replace(Object_INDEX_RE, (orignal, matched) => {
248
- const value = values[matched];
249
- if (value) {
250
- return value;
251
- }
252
- // not match index, return orignal text
253
- return orignal;
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
- if (!locale) return "";
258
- return locale.replace(/_/g, "-").toLowerCase();
229
+ if (!locale)
230
+ return "";
231
+ return locale.replace(/_/g, "-").toLowerCase();
259
232
  }
260
233
  function flattening(data) {
261
- const result = {};
262
- function deepFlat(data, keys) {
263
- Object.keys(data).forEach((key) => {
264
- const value = data[key];
265
- const k = keys ? keys + "." + key : key;
266
- if (isObject(value)) {
267
- deepFlat(value, k);
268
- } else {
269
- result[k] = String(value);
270
- }
271
- });
272
- }
273
- deepFlat(data, "");
274
- return result;
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
- module.exports = locales;
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.2",
3
+ "version": "2.1.1",
4
4
  "description": "koa locales, i18n solution for koa",
5
5
  "keywords": [
6
6
  "koa-locales",
@@ -23,17 +23,19 @@
23
23
  "type": "module",
24
24
  "exports": {
25
25
  "import": "./dist/esm/index.js",
26
- "require": "./dist/esm/index.js",
26
+ "require": "./dist/cjs/index.cjs",
27
27
  "types": "./dist/types/index.d.ts"
28
28
  },
29
- "main": "./dist/esm/index.js",
29
+ "main": "./dist/cjs/index.js",
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",