@sap/cds 8.8.1 → 8.8.2
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/CHANGELOG.md +7 -0
- package/lib/i18n/bundles.js +17 -17
- package/lib/i18n/files.js +5 -3
- package/lib/i18n/index.js +1 -1
- package/lib/i18n/localize.js +2 -2
- package/libx/_runtime/remote/Service.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,13 @@
|
|
|
4
4
|
- The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
5
5
|
- This project adheres to [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## Version 8.8.2 - 2025-03-13
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Consuming REST actions returning anonymous structures
|
|
12
|
+
- `i18n.labels/messages` were occasionally missing
|
|
13
|
+
|
|
7
14
|
## Version 8.8.1 - 2025-03-07
|
|
8
15
|
|
|
9
16
|
### Fixed
|
package/lib/i18n/bundles.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const cds = require('..'), {i18n} = cds.env
|
|
2
2
|
const I18nFiles = require ('./files')
|
|
3
|
+
const DEFAULTS = i18n.default_language
|
|
4
|
+
const FALLBACK = ''
|
|
3
5
|
|
|
4
6
|
|
|
5
7
|
class I18nBundle {
|
|
@@ -7,14 +9,14 @@ class I18nBundle {
|
|
|
7
9
|
constructor (options={}) {
|
|
8
10
|
this.files = new I18nFiles (options)
|
|
9
11
|
this.file = this.files.basename
|
|
12
|
+
this.fallback = this.#translations[FALLBACK] = Object.assign ({}, ...this.files.content4(FALLBACK))
|
|
13
|
+
this.defaults = this.#translations[DEFAULTS] = Object.assign (
|
|
14
|
+
i18n.fatjson ? {...this.fallback} : {__proto__:this.fallback}, ...this.files.content4(DEFAULTS)
|
|
15
|
+
)
|
|
10
16
|
}
|
|
11
17
|
|
|
12
18
|
#translations = {}
|
|
13
19
|
|
|
14
|
-
/** The default texts to use as fallbacks if a requested translation is not found. */
|
|
15
|
-
get defaults() { return super.defaults = this.texts4 (i18n.default_language, this.fallback) }
|
|
16
|
-
get fallback() { return super.fallback = this.texts4 ('',false) }
|
|
17
|
-
|
|
18
20
|
/** Synonym for {@link at `this.at`} */
|
|
19
21
|
get for() { return this.at }
|
|
20
22
|
|
|
@@ -23,6 +25,7 @@ class I18nBundle {
|
|
|
23
25
|
* Looks up the entry for the given key and locale.
|
|
24
26
|
* - if `locale` is omitted, the current locale is used.
|
|
25
27
|
* - if `args` are provided, fills in placeholders with them.
|
|
28
|
+
* @example cds.i18n.labels.at ('CreatedAt','de')
|
|
26
29
|
* @returns {string|undefined}
|
|
27
30
|
*/
|
|
28
31
|
at (key, locale, args) {
|
|
@@ -62,32 +65,29 @@ class I18nBundle {
|
|
|
62
65
|
|
|
63
66
|
/**
|
|
64
67
|
* Returns translated texts for a specific locale.
|
|
68
|
+
* @example cds.i18n.labels.texts4 ('de')
|
|
65
69
|
*/
|
|
66
|
-
texts4 (locale=''
|
|
70
|
+
texts4 (locale='') {
|
|
67
71
|
const $ = this.#translations; if (locale in $) return $[locale]
|
|
68
72
|
const suffix = locale.replace(/-/g,'_'); if (suffix in $) return $[locale] = $[suffix]
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (lang in $) return $[locale] = $[lang] // already cached -> leave method
|
|
74
|
-
else all = this.files.content4 (lang, lang) // load content for language only
|
|
73
|
+
const all = this.files.content4 (locale, suffix) // load content from all folders
|
|
74
|
+
if (!all.length) { // nothing found, try w/o region, or return defaults
|
|
75
|
+
const _ = suffix.indexOf('_')
|
|
76
|
+
return $[locale] = $[suffix] = _ < 0 ? this.defaults : this.texts4 (suffix.slice(0,_))
|
|
75
77
|
}
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
return $[locale] = $[suffix] = Object.assign (texts, ...all)
|
|
78
|
+
const texts = i18n.fatjson ? {...this.defaults} : {__proto__:this.defaults}
|
|
79
|
+
return $[locale] = $[suffix] = Object.assign (texts, ...all )
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
|
|
82
83
|
/**
|
|
83
84
|
* Returns all translations for an array of locales or all locales.
|
|
84
|
-
* @example { de, fr } = cds.i18n.labels.
|
|
85
|
+
* @example { de, fr } = cds.i18n.labels.translations4 ('de','fr')
|
|
85
86
|
* @param { 'all' | string[] } [locale]
|
|
86
87
|
* @returns {{ [locale:string]: Record<string,string> }}
|
|
87
88
|
*/
|
|
88
89
|
translations4 (...locales) {
|
|
89
|
-
let first = locales[0]
|
|
90
|
-
if (first == null) locales = cds.env.i18n.languages
|
|
90
|
+
let first = locales[0] || cds.env.i18n.languages
|
|
91
91
|
if (first == 'all') locales = this.files.locales()
|
|
92
92
|
else if (Array.isArray(first)) locales = first
|
|
93
93
|
return locales.reduce ((all,l) => (all[l] = this.texts4(l),all), {})
|
package/lib/i18n/files.js
CHANGED
|
@@ -36,7 +36,7 @@ class I18nFiles {
|
|
|
36
36
|
const leafs = model?.$sources.map(path.dirname) ?? roots, visited = {}
|
|
37
37
|
;[...new Set(leafs)].reverse() .forEach (function _visit (dir) {
|
|
38
38
|
if (dir in visited) return; else visited[dir] = true
|
|
39
|
-
LOG.debug ('
|
|
39
|
+
LOG.debug ('fetching', basename, 'bundles in', dir, relative_folders)
|
|
40
40
|
// is there an i18n folder in the currently visited directory?
|
|
41
41
|
for (const each of relative_folders) {
|
|
42
42
|
const f = path.join(dir,each), _exists = _folders[f] ??= exists(f)
|
|
@@ -60,6 +60,8 @@ class I18nFiles {
|
|
|
60
60
|
const matches = (_entries[f] ??= fs.readdirSync(f)) .filter (f => f.match(base))
|
|
61
61
|
if (matches.length) return files[f] = matches
|
|
62
62
|
}
|
|
63
|
+
|
|
64
|
+
LOG.debug ('found', basename, 'bundles in these folders', Object.keys(files))
|
|
63
65
|
}
|
|
64
66
|
|
|
65
67
|
|
|
@@ -68,7 +70,7 @@ class I18nFiles {
|
|
|
68
70
|
* @returns {entries[]} An array of entries, one for each file found.
|
|
69
71
|
*/
|
|
70
72
|
content4 (locale, suffix = locale?.replace(/-/g,'_')) {
|
|
71
|
-
const content = [], cached = I18nFiles.
|
|
73
|
+
const content = [], cached = I18nFiles[this.basename] ??= {}
|
|
72
74
|
const _suffix = suffix ? '_'+ suffix : ''
|
|
73
75
|
for (let dir in this) {
|
|
74
76
|
const all = cached[dir] ??= this.load('.json',dir) || this.load('.csv',dir) || false
|
|
@@ -87,7 +89,7 @@ class I18nFiles {
|
|
|
87
89
|
case '.json': return _load_json(file)
|
|
88
90
|
case '.csv': return _load_csv(file)
|
|
89
91
|
}}
|
|
90
|
-
finally { LOG.debug ('
|
|
92
|
+
finally { LOG.debug ('loading:', file) }
|
|
91
93
|
}
|
|
92
94
|
|
|
93
95
|
|
package/lib/i18n/index.js
CHANGED
package/lib/i18n/localize.js
CHANGED
|
@@ -62,7 +62,7 @@ function localize (input,...etc) {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
exports.edmx4 = service => new class extends Localize { get input(){
|
|
65
|
-
const model = this.model || cds.
|
|
65
|
+
const model = this.model || cds.model
|
|
66
66
|
return super.input = cds.compile.to.edmx (model,{service})
|
|
67
67
|
}}
|
|
68
68
|
|
|
@@ -124,7 +124,7 @@ exports.json = json => {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
/** @deprecated */ exports.bundle4 = function (model, locale) {
|
|
127
|
-
if (typeof model === 'string') [ model, locale ] = [ cds.
|
|
127
|
+
if (typeof model === 'string') [ model, locale ] = [ cds.model, model ]
|
|
128
128
|
const b = i18n.bundle4 (model)
|
|
129
129
|
return b.texts4 (locale)
|
|
130
130
|
}
|
|
@@ -93,7 +93,7 @@ const _handleUnboundActionFunction = (srv, def, req, event) => {
|
|
|
93
93
|
if (def.kind === 'action') {
|
|
94
94
|
// REVISIT: only for "rest" unbound actions/functions, we enforce axios to return a buffer
|
|
95
95
|
// required by cds-mt
|
|
96
|
-
const isBinary = srv.kind === 'rest' && def?.returns?.type
|
|
96
|
+
const isBinary = srv.kind === 'rest' && def?.returns?.type?.match(/binary/i)
|
|
97
97
|
const { headers, data } = req
|
|
98
98
|
|
|
99
99
|
return srv.send({ method: 'POST', path: `/${event}`, headers, data, _binary: isBinary })
|