smartloc 2.0.2 → 2.0.3
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/adapters/adapter-base.d.ts +12 -0
- package/adapters/adapter-base.js +56 -0
- package/adapters/adapter-base.js.map +1 -0
- package/adapters/index.d.ts +4 -0
- package/adapters/index.js +32 -0
- package/adapters/index.js.map +1 -0
- package/adapters/json.d.ts +6 -0
- package/adapters/json.js +23 -0
- package/adapters/json.js.map +1 -0
- package/adapters/xliff.d.ts +6 -0
- package/adapters/xliff.js +35 -0
- package/adapters/xliff.js.map +1 -0
- package/cli/cli.d.ts +2 -0
- package/cli/cli.js +129 -0
- package/cli/cli.js.map +1 -0
- package/cli/collect.d.ts +14 -0
- package/{src/cli/collect.ts → cli/collect.js} +59 -75
- package/cli/collect.js.map +1 -0
- package/cli/interfaces.js +3 -0
- package/cli/interfaces.js.map +1 -0
- package/cli/reconciliate.d.ts +6 -0
- package/{src/cli/reconciliate.ts → cli/reconciliate.js} +35 -40
- package/cli/reconciliate.js.map +1 -0
- package/cli/utils.d.ts +6 -0
- package/{src/cli/utils.ts → cli/utils.js} +16 -12
- package/cli/utils.js.map +1 -0
- package/core/default-locale.d.ts +10 -0
- package/{src/core/default-locale.tsx → core/default-locale.js} +23 -28
- package/core/default-locale.js.map +1 -0
- package/{src/core/interfaces.ts → core/interfaces.d.ts} +15 -20
- package/core/interfaces.js +3 -0
- package/core/interfaces.js.map +1 -0
- package/core/json-utils.d.ts +19 -0
- package/{src/core/json-utils.ts → core/json-utils.js} +27 -33
- package/core/json-utils.js.map +1 -0
- package/core/literal.d.ts +13 -0
- package/{src/core/literal.ts → core/literal.js} +20 -19
- package/core/literal.js.map +1 -0
- package/core/load.d.ts +32 -0
- package/core/load.js +50 -0
- package/core/load.js.map +1 -0
- package/core/locale-list.d.ts +27 -0
- package/{src/core/locale-list.ts → core/locale-list.js} +35 -38
- package/core/locale-list.js.map +1 -0
- package/core/locale.d.ts +15 -0
- package/{src/core/locale.tsx → core/locale.js} +29 -28
- package/core/locale.js.map +1 -0
- package/core/smartloc.d.ts +39 -0
- package/{src/core/smartloc.tsx → core/smartloc.js} +104 -122
- package/core/smartloc.js.map +1 -0
- package/core/tag.d.ts +13 -0
- package/core/tag.js +28 -0
- package/core/tag.js.map +1 -0
- package/core/utils.d.ts +4 -0
- package/core/utils.js +30 -0
- package/core/utils.js.map +1 -0
- package/express.d.ts +12 -0
- package/{src/express.ts → express.js} +33 -41
- package/express.js.map +1 -0
- package/graphql.d.ts +13 -0
- package/{src/graphql.ts → graphql.js} +62 -74
- package/graphql.js.map +1 -0
- package/index.js +30 -1
- package/index.js.map +1 -0
- package/node/index.d.ts +1 -0
- package/node/index.js +18 -0
- package/node/index.js.map +1 -0
- package/node/load.d.ts +12 -0
- package/node/load.js +37 -0
- package/node/load.js.map +1 -0
- package/package.json +3 -22
- package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +0 -111
- package/.vscode/launch.json +0 -65
- package/.vscode/settings.json +0 -14
- package/.vscode/tasks.json +0 -27
- package/build/README.md +0 -225
- package/build/package.json +0 -60
- package/bun.lock +0 -310
- package/samples/express/main.ts +0 -37
- package/samples/express/package.json +0 -10
- package/samples/graphql/main.ts +0 -73
- package/samples/graphql/package.json +0 -15
- package/src/adapters/adapter-base.ts +0 -58
- package/src/adapters/index.ts +0 -31
- package/src/adapters/json.ts +0 -18
- package/src/adapters/xliff.ts +0 -30
- package/src/cli/cli.ts +0 -137
- package/src/core/load.ts +0 -82
- package/src/core/tag.ts +0 -48
- package/src/core/utils.tsx +0 -24
- package/src/node/index.ts +0 -1
- package/src/node/load.ts +0 -32
- package/test/collect.spec.ts +0 -45
- package/test/reconciliation.spec.ts +0 -132
- package/test/serialization.spec.ts +0 -162
- package/test/translation.spec.ts +0 -61
- package/tsconfig.json +0 -37
- package/tslint.json +0 -103
- /package/{src/cli/interfaces.ts → cli/interfaces.d.ts} +0 -0
- /package/{src/index.ts → index.d.ts} +0 -0
package/src/cli/cli.ts
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
|
|
3
|
-
import yargs, { Argv } from 'yargs';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import { collect } from './collect';
|
|
6
|
-
import { reconciliate } from './reconciliate';
|
|
7
|
-
import { getAdapter } from '../adapters';
|
|
8
|
-
|
|
9
|
-
// === arguments parsing ===
|
|
10
|
-
|
|
11
|
-
function isValidLocale(x: string) {
|
|
12
|
-
return typeof x === 'string' && !!x && /^[a-zA-Z]+(\-[a-zA-Z]+)?$/.test(x);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function defaultArgs<T>(b: Argv<T>): Argv<T & { format: string; outDir: string; }> {
|
|
16
|
-
return b.positional('format', {
|
|
17
|
-
describe: 'The output format',
|
|
18
|
-
choices: ['json', 'xliff'],
|
|
19
|
-
default: 'xliff',
|
|
20
|
-
})
|
|
21
|
-
.positional('outDir', {
|
|
22
|
-
describe: 'The translation output directory',
|
|
23
|
-
default: 'i18n',
|
|
24
|
-
normalize: true,
|
|
25
|
-
})
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
yargs
|
|
29
|
-
.scriptName('smartloc')
|
|
30
|
-
.command('add', 'Add a new locale', b => defaultArgs(b).demandCommand()
|
|
31
|
-
, args => {
|
|
32
|
-
console.log(args);
|
|
33
|
-
})
|
|
34
|
-
.command('collect', 'Collects all strings to translate', b => defaultArgs(b)
|
|
35
|
-
.positional('source', {
|
|
36
|
-
describe: 'The source directory where to look for translations (defaults to current working directory)',
|
|
37
|
-
type: 'string',
|
|
38
|
-
normalize: true,
|
|
39
|
-
})
|
|
40
|
-
.positional('defaultLocale', {
|
|
41
|
-
description: 'Sets the default locale (the locale used in code for your strings)',
|
|
42
|
-
type: 'string',
|
|
43
|
-
coerce: x => {
|
|
44
|
-
if (!isValidLocale(x)) {
|
|
45
|
-
console.error('Invalid locale ' + x);
|
|
46
|
-
process.exit(1);
|
|
47
|
-
}
|
|
48
|
-
return x;
|
|
49
|
-
},
|
|
50
|
-
})
|
|
51
|
-
.positional('generateDefault', {
|
|
52
|
-
description: 'Also generates a translation file for the default locale',
|
|
53
|
-
type: 'boolean',
|
|
54
|
-
})
|
|
55
|
-
.positional('generateDefault', {
|
|
56
|
-
description: 'Also generates the default locale translation file (which does not need to be translated)',
|
|
57
|
-
type: 'boolean',
|
|
58
|
-
})
|
|
59
|
-
.positional('locales', {
|
|
60
|
-
description: 'Comma separated list of locales to generate (others than the default one)',
|
|
61
|
-
type: 'string',
|
|
62
|
-
coerce: x => {
|
|
63
|
-
if (typeof x !== 'string' || !x || !/^[a-zA-Z]+(\-[a-zA-Z]+)?$/.test(x)) {
|
|
64
|
-
console.error('Invalid locale ' + x);
|
|
65
|
-
process.exit(1);
|
|
66
|
-
}
|
|
67
|
-
return x;
|
|
68
|
-
},
|
|
69
|
-
})
|
|
70
|
-
, async args => {
|
|
71
|
-
try {
|
|
72
|
-
|
|
73
|
-
const locales = args.locales?.split(',')
|
|
74
|
-
.map(x => x.trim())
|
|
75
|
-
.filter(x => x !== args.defaultLocale);
|
|
76
|
-
if (!locales?.length) {
|
|
77
|
-
console.error('You must provide at least one locale thats different from your default locale')
|
|
78
|
-
process.exit(1);
|
|
79
|
-
}
|
|
80
|
-
if (locales.some(x => !isValidLocale(x))) {
|
|
81
|
-
console.error('--locales argument is invalid')
|
|
82
|
-
process.exit(1);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// create adapter
|
|
86
|
-
const outdir = path.join(process.cwd(), args.outDir);
|
|
87
|
-
const adapter = getAdapter(args.format, outdir);
|
|
88
|
-
|
|
89
|
-
// collect from sources
|
|
90
|
-
console.log('Collecting from sources...');
|
|
91
|
-
const { collected, files } = await collect(args.source || '.', adapter, args.defaultLocale);
|
|
92
|
-
console.log(`...found ${Object.keys(collected.resources).length} translatable items over ${files} files with default language ${collected.targetLanguage}`);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// load other locales
|
|
96
|
-
const others = (await adapter.loadLocales(locales))
|
|
97
|
-
.filter(x => x.targetLanguage !== collected.targetLanguage);
|
|
98
|
-
|
|
99
|
-
console.log(`Updating ${others.length} other translations...`);
|
|
100
|
-
|
|
101
|
-
// reconciliate existing
|
|
102
|
-
for (const l of others) {
|
|
103
|
-
const collectedCopy = JSON.parse(JSON.stringify(collected));
|
|
104
|
-
const { dirty, missing, same } = reconciliate(l, collectedCopy);
|
|
105
|
-
const feats: string[] = [];
|
|
106
|
-
if (dirty) {
|
|
107
|
-
feats.push(`${dirty} dirty translations (source changed)`);
|
|
108
|
-
}
|
|
109
|
-
if (missing) {
|
|
110
|
-
feats.push(`${missing} missing translations`);
|
|
111
|
-
}
|
|
112
|
-
if (same.length) {
|
|
113
|
-
feats.push(`${same.length} translations matching original (ex: ${same[0]})`);
|
|
114
|
-
}
|
|
115
|
-
if (!feats.length) {
|
|
116
|
-
console.log(`${l.targetLanguage} is OK ♥`);
|
|
117
|
-
} else {
|
|
118
|
-
console.log(`${l.targetLanguage} has ${feats.join(', ')}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// write others
|
|
123
|
-
console.log(`Writing results...`);
|
|
124
|
-
for (const l of others) {
|
|
125
|
-
await adapter.write(l);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (args.defaultLocale) {
|
|
129
|
-
await adapter.write(collected);
|
|
130
|
-
}
|
|
131
|
-
console.log(`I'm done ♥`);
|
|
132
|
-
} catch (e) {
|
|
133
|
-
console.error('Command failed', e);
|
|
134
|
-
process.exit(1);
|
|
135
|
-
}
|
|
136
|
-
})
|
|
137
|
-
.argv;
|
package/src/core/load.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import { addLocale } from './locale-list';
|
|
2
|
-
import type { TranslationTarget } from './interfaces';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Loads a locale generated from CLI
|
|
6
|
-
* @param filePath The file path to load
|
|
7
|
-
* @param merge If true, then the translations will be merged with already existing translations.
|
|
8
|
-
*/
|
|
9
|
-
export function loadJsonLocale(obj: GroupedTranslation, merge?: boolean): void
|
|
10
|
-
export function loadJsonLocale(url: string, merge?: boolean): Promise<void>
|
|
11
|
-
export function loadJsonLocale(urlOrObj: string | GroupedTranslation, merge?: boolean) {
|
|
12
|
-
if (typeof urlOrObj === 'string') {
|
|
13
|
-
return fetch(urlOrObj).then(async response => {
|
|
14
|
-
if (!response.ok) {
|
|
15
|
-
throw new Error(`Failed to fetch ${urlOrObj}: ${response.statusText}`);
|
|
16
|
-
}
|
|
17
|
-
const text = await response.json();
|
|
18
|
-
if (typeof text !== 'object') {
|
|
19
|
-
throw new Error(`Expecting ${urlOrObj} to be a JSON object`);
|
|
20
|
-
}
|
|
21
|
-
return loadJsonLocale(text, merge);
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const transformed = convertLocale(ungroupTranslation(urlOrObj));
|
|
26
|
-
addLocale(urlOrObj.targetLanguage, transformed, merge);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export interface CollectItem {
|
|
31
|
-
source?: TranslationTarget;
|
|
32
|
-
target?: TranslationTarget | null;
|
|
33
|
-
dirty?: boolean;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface CollectResult {
|
|
37
|
-
[id: string]: CollectItem;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface Translation {
|
|
41
|
-
resources: CollectResult;
|
|
42
|
-
sourceLanguage?: string;
|
|
43
|
-
targetLanguage: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
export interface GroupedTranslation {
|
|
48
|
-
resources: { [namespace: string]: CollectResult };
|
|
49
|
-
sourceLanguage?: string;
|
|
50
|
-
targetLanguage: string;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
export function ungroupTranslation(translation: GroupedTranslation): Translation {
|
|
55
|
-
const result: Translation = {
|
|
56
|
-
targetLanguage: translation.targetLanguage,
|
|
57
|
-
sourceLanguage: translation.sourceLanguage,
|
|
58
|
-
resources: {},
|
|
59
|
-
};
|
|
60
|
-
for (const [namespace, data] of Object.entries(translation.resources)) {
|
|
61
|
-
for (const [id, val] of Object.entries(data ?? {})) {
|
|
62
|
-
if (namespace === '$default') {
|
|
63
|
-
result.resources[id] = val;
|
|
64
|
-
} else {
|
|
65
|
-
result.resources[`${namespace}.${id}`] = val;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return result;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
export function convertLocale(locale: Translation): { [key: string]: TranslationTarget } {
|
|
75
|
-
const ret: { [key: string]: TranslationTarget } = {};
|
|
76
|
-
for (const [k, v] of Object.entries(locale.resources)) {
|
|
77
|
-
if (v.target) {
|
|
78
|
-
ret[k] = v.target;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
return ret;
|
|
82
|
-
}
|
package/src/core/tag.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { smartLoc } from './smartloc';
|
|
2
|
-
import { LocLiteral, LocStr } from './interfaces';
|
|
3
|
-
import { autoGenerateId } from './utils';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
type Templater = (literals: TemplateStringsArray, ...placeholders: LocLiteral[]) => LocStr;
|
|
7
|
-
// just a signature.
|
|
8
|
-
declare function _tagSig(id: string): Templater
|
|
9
|
-
declare function _tagSig(literals: TemplateStringsArray, ...placeholders: LocLiteral[]): LocStr;
|
|
10
|
-
type TagSignature = typeof _tagSig;
|
|
11
|
-
type LocTag = TagSignature & {
|
|
12
|
-
/** Creates a pluralizable string (will have two versions to be translated based on the given count: singlular & plural) */
|
|
13
|
-
plural(count: number): TagSignature;
|
|
14
|
-
/** Does nothing, except that the translation will not be collected (use for formatting purposes) */
|
|
15
|
-
nocollect: LocTag
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
function __loc(count: number | undefined, idOrLiterals: string | TemplateStringsArray, ...ph: LocLiteral[]): Templater | LocStr {
|
|
22
|
-
if (typeof idOrLiterals === 'string') {
|
|
23
|
-
const id = idOrLiterals;
|
|
24
|
-
return (literals: TemplateStringsArray, ...placeholders: LocLiteral[]) => {
|
|
25
|
-
return smartLoc({ id, literals, placeholders, count });
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
return smartLoc({ id: autoGenerateId(idOrLiterals), literals: idOrLiterals, placeholders: ph, count });
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function _loc(id: string): Templater;
|
|
32
|
-
function _loc(idOrLiteralsOrCount: string | TemplateStringsArray, ...ph: LocLiteral[]): LocStr;
|
|
33
|
-
function _loc(idOrLiteralsOrCount: string | TemplateStringsArray, ...ph: LocLiteral[]): Templater | LocStr {
|
|
34
|
-
return __loc(undefined, idOrLiteralsOrCount, ...ph);
|
|
35
|
-
}
|
|
36
|
-
_loc.plural = (count: number) => {
|
|
37
|
-
function templater(id: string): Templater;
|
|
38
|
-
function templater(template: TemplateStringsArray, ...placeholders: LocLiteral[]): Templater;
|
|
39
|
-
function templater(idOrLiterals: string | TemplateStringsArray, ...ph: LocLiteral[]): Templater | LocStr {
|
|
40
|
-
return __loc(count, idOrLiterals, ...ph);
|
|
41
|
-
}
|
|
42
|
-
return templater;
|
|
43
|
-
};
|
|
44
|
-
_loc.nocollect = () => {
|
|
45
|
-
return _loc;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
export const loc: LocTag = _loc as any;
|
package/src/core/utils.tsx
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { sha1 } from 'js-sha1';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
|
|
4
|
-
export function autoGenerateId(str: TemplateStringsArray | string[]): string {
|
|
5
|
-
const sha = sha1(str.join('|'));
|
|
6
|
-
return 'sha1.' + sha;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function getLocaleCode(locale: string) {
|
|
10
|
-
return locale && /^[a-z]+/.exec(locale.toLowerCase())?.[0] || undefined;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export function asFragment(children: React.ReactNode[]): React.ReactNode {
|
|
15
|
-
if (children.length === 1) {
|
|
16
|
-
return children[0];
|
|
17
|
-
}
|
|
18
|
-
return <React.Fragment>{children.map((x, i) => {
|
|
19
|
-
if (React.isValidElement(x) && !x.key) {
|
|
20
|
-
return <React.Fragment key={i}>{x}</React.Fragment>;
|
|
21
|
-
}
|
|
22
|
-
return x;
|
|
23
|
-
})}</React.Fragment>;
|
|
24
|
-
}
|
package/src/node/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './load';
|
package/src/node/load.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import fs from 'fs-extra';
|
|
3
|
-
import { loadLocaleFrom } from '../adapters';
|
|
4
|
-
import { addLocale } from '../core/locale-list';
|
|
5
|
-
import { convertLocale } from 'src/core/load';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Loads all locales generated from CLI
|
|
9
|
-
* @param dirPath The file directory where to look for translation
|
|
10
|
-
* @param merge If true, then the translations will be merged with already existing translations.
|
|
11
|
-
*/
|
|
12
|
-
export async function loadAllLocales(dirPath: string, merge?: boolean) {
|
|
13
|
-
for (const f of await fs.readdir(dirPath)) {
|
|
14
|
-
await loadLocale(path.join(dirPath, f), merge);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Loads a locale generated from CLI
|
|
21
|
-
* @param filePath The file path to load
|
|
22
|
-
* @param merge If true, then the translations will be merged with already existing translations.
|
|
23
|
-
*/
|
|
24
|
-
export async function loadLocale(filePath: string, merge?: boolean) {
|
|
25
|
-
const locale = await loadLocaleFrom(filePath);
|
|
26
|
-
if (!locale) {
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
const transformed = convertLocale(locale);
|
|
30
|
-
addLocale(locale.targetLanguage, transformed, merge);
|
|
31
|
-
return true;
|
|
32
|
-
}
|
package/test/collect.spec.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'bun:test';
|
|
2
|
-
import { collectFromSource, Loc } from '../src/cli/collect';
|
|
3
|
-
import { autoGenerateId } from '../src/core/utils';
|
|
4
|
-
|
|
5
|
-
describe('Collection', () => {
|
|
6
|
-
function col(content: string) {
|
|
7
|
-
const all: Loc[] = [];
|
|
8
|
-
const ids = new Map();
|
|
9
|
-
collectFromSource(content, ids, all);
|
|
10
|
-
return all.map(x => {
|
|
11
|
-
const ret: { id: string; val: string; plural?: boolean } = {
|
|
12
|
-
id: x.id,
|
|
13
|
-
val: x.source,
|
|
14
|
-
};
|
|
15
|
-
if (x.plural) {
|
|
16
|
-
ret.plural = x.plural;
|
|
17
|
-
}
|
|
18
|
-
return ret;
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
it('collect with id', () => {
|
|
23
|
-
const all = col('blah`` loc("myId") `String ${with} some ${params}`');
|
|
24
|
-
expect(all).toEqual([
|
|
25
|
-
{ id: 'myId', val: 'String {0} some {1}' }
|
|
26
|
-
])
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
it('collect without id', () => {
|
|
31
|
-
const all = col('blah`` loc `String ${with} some ${params}`');
|
|
32
|
-
const id = autoGenerateId(['String ', ' some ', '']);
|
|
33
|
-
expect(all).toEqual([
|
|
34
|
-
{ id, val: 'String {0} some {1}' }
|
|
35
|
-
])
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('collects plural', () => {
|
|
39
|
-
const all = col('blah`` loc.plural(42) `String ${with} some ${params}`');
|
|
40
|
-
const id = autoGenerateId(['String ', ' some ', '']);
|
|
41
|
-
expect(all).toEqual([
|
|
42
|
-
{ id, val: 'String {0} some {1}', plural: true }
|
|
43
|
-
])
|
|
44
|
-
});
|
|
45
|
-
});
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'bun:test';
|
|
2
|
-
import { reconciliate } from '../src/cli/reconciliate';
|
|
3
|
-
import { CollectResult } from '../src/cli/interfaces';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
describe('Reconciliation', () => {
|
|
7
|
-
|
|
8
|
-
function recon(translated: CollectResult, orig: CollectResult) {
|
|
9
|
-
return reconciliate({
|
|
10
|
-
resources: translated,
|
|
11
|
-
sourceLanguage: 'en',
|
|
12
|
-
targetLanguage: 'fr',
|
|
13
|
-
}, {
|
|
14
|
-
resources: orig,
|
|
15
|
-
targetLanguage: 'en',
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function setupAB() {
|
|
20
|
-
const orig: CollectResult = {
|
|
21
|
-
a: { target: 'Sentence A' },
|
|
22
|
-
b: { target: 'Sentence B' },
|
|
23
|
-
};
|
|
24
|
-
const trans: CollectResult = {
|
|
25
|
-
a: { source: 'Sentence A', target: 'Phrase A' },
|
|
26
|
-
b: { source: 'Sentence B', target: 'Phrase B' },
|
|
27
|
-
};
|
|
28
|
-
return { orig, trans };
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
it('does nothing when ok', () => {
|
|
32
|
-
const { orig, trans } = setupAB();
|
|
33
|
-
const { dirty, missing, same } = recon(trans, orig);
|
|
34
|
-
expect(dirty).toEqual(0);
|
|
35
|
-
expect(missing).toEqual(0);
|
|
36
|
-
expect(same).toEqual([]);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
it('marks as dirty', () => {
|
|
41
|
-
const { orig, trans } = setupAB();
|
|
42
|
-
|
|
43
|
-
// change 'in code' sentence
|
|
44
|
-
orig.a.target = 'New sentence A';
|
|
45
|
-
|
|
46
|
-
const { dirty, missing, same } = recon(trans, orig);
|
|
47
|
-
expect(dirty).toEqual(1);
|
|
48
|
-
expect(missing).toEqual(0);
|
|
49
|
-
expect(same).toEqual([]);
|
|
50
|
-
expect(trans.a.dirty).toBe(true);
|
|
51
|
-
expect(trans.a.source).toEqual('New sentence A');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
it('adds new sentence', () => {
|
|
56
|
-
const { orig, trans } = setupAB();
|
|
57
|
-
|
|
58
|
-
// add new 'in code' sentence
|
|
59
|
-
orig.c = { target: 'Sentence C' };
|
|
60
|
-
|
|
61
|
-
const { dirty, missing, same } = recon(trans, orig);
|
|
62
|
-
expect(dirty).toEqual(0);
|
|
63
|
-
expect(missing).toEqual(1);
|
|
64
|
-
expect(same).toEqual([]);
|
|
65
|
-
expect(trans.c.source).toEqual('Sentence C');
|
|
66
|
-
expect(trans.c.target).toBeUndefined();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
it('finds on ID change', () => {
|
|
72
|
-
const { orig, trans } = setupAB();
|
|
73
|
-
|
|
74
|
-
// add new 'in code' sentence
|
|
75
|
-
orig.newb = orig.b;
|
|
76
|
-
delete orig.b;
|
|
77
|
-
|
|
78
|
-
const { dirty, missing, same } = recon(trans, orig);
|
|
79
|
-
expect(dirty).toEqual(0);
|
|
80
|
-
expect(missing).toEqual(0);
|
|
81
|
-
expect(same).toEqual([]);
|
|
82
|
-
expect(trans.b).toBeUndefined();
|
|
83
|
-
expect(trans.newb).toBeDefined();
|
|
84
|
-
expect(trans.newb.source).toEqual('Sentence B');
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
it('finds on ID and content change change', () => {
|
|
89
|
-
const { orig, trans } = setupAB();
|
|
90
|
-
|
|
91
|
-
// add new 'in code' sentence
|
|
92
|
-
orig.newb = orig.b;
|
|
93
|
-
orig.newb.target = 'Sentence B bis';
|
|
94
|
-
delete orig.b;
|
|
95
|
-
|
|
96
|
-
const { dirty, missing, same } = recon(trans, orig);
|
|
97
|
-
expect(dirty).toEqual(1); // source changed => dirty
|
|
98
|
-
expect(missing).toEqual(0);
|
|
99
|
-
expect(same).toEqual([]);
|
|
100
|
-
expect(trans.b).toBeUndefined();
|
|
101
|
-
expect(trans.newb).toBeDefined();
|
|
102
|
-
expect(trans.newb.source).toEqual('Sentence B bis');
|
|
103
|
-
expect(trans.newb.target).toEqual('Phrase B');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
it('finds multiple ID and content change', () => {
|
|
109
|
-
const { orig, trans } = setupAB();
|
|
110
|
-
|
|
111
|
-
// add new 'in code' sentence
|
|
112
|
-
orig.newa = orig.a;
|
|
113
|
-
orig.newb = orig.b;
|
|
114
|
-
orig.newa.target = 'Sentence A bis';
|
|
115
|
-
orig.newb.target = 'Sentence B bis';
|
|
116
|
-
delete orig.b;
|
|
117
|
-
delete orig.a;
|
|
118
|
-
|
|
119
|
-
const { dirty, missing, same } = recon(trans, orig);
|
|
120
|
-
expect(dirty).toEqual(2); // source changed => dirty
|
|
121
|
-
expect(missing).toEqual(0);
|
|
122
|
-
expect(same).toEqual([]);
|
|
123
|
-
expect(trans.a).toBeUndefined();
|
|
124
|
-
expect(trans.b).toBeUndefined();
|
|
125
|
-
expect(trans.newa).toBeDefined();
|
|
126
|
-
expect(trans.newb).toBeDefined();
|
|
127
|
-
expect(trans.newa.source).toEqual('Sentence A bis');
|
|
128
|
-
expect(trans.newb.source).toEqual('Sentence B bis');
|
|
129
|
-
expect(trans.newa.target).toEqual('Phrase A');
|
|
130
|
-
expect(trans.newb.target).toEqual('Phrase B');
|
|
131
|
-
});
|
|
132
|
-
});
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from 'bun:test';
|
|
2
|
-
import { loc, setDefaultLocale, addLocale, withLocales, clearLocales, toJsonStorable, SingleLoc, MultiLoc, jsonParseLocalized, toLocalizable } from '../src';
|
|
3
|
-
import { withSerializationContext, SmartLoc } from '../src/core/smartloc';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
describe('Json serialization', () => {
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
clearLocales();
|
|
10
|
-
setDefaultLocale('en-US');
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
it('toJsonStorable() handles singleloc values', () => {
|
|
14
|
-
const str = new SingleLoc(`a translation`);
|
|
15
|
-
expect(toJsonStorable(str)).toEqual('i18n/single:a translation')
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('toJsonStorable() handles singleloc props', () => {
|
|
19
|
-
const str = { value: new SingleLoc(`a translation`) };
|
|
20
|
-
expect(toJsonStorable(str)).toEqual({ value: 'i18n/single:a translation' })
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
it('toJsonStorable() handles singleloc arrays', () => {
|
|
25
|
-
const str = { value: [new SingleLoc(`first`), new SingleLoc(`second`)] };
|
|
26
|
-
expect(toJsonStorable(str)).toEqual({ value: ['i18n/single:first', 'i18n/single:second'] })
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
it('toJsonStorable() handles multiloc', () => {
|
|
31
|
-
const str = { value: new MultiLoc({ fr: 'FR value', en: 'EN value' }) };
|
|
32
|
-
expect(toJsonStorable(str)).toEqual({ value: { 'i18n:fr': 'FR value', 'i18n:en': 'EN value', } })
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
it('toJsonStorable() handles smartloc with translate option to "id"', () => {
|
|
37
|
-
const str = { value: loc('something')`Something` };
|
|
38
|
-
const received = toJsonStorable(str, { nonSelfDescriptive: 'id' });
|
|
39
|
-
expect(received).toEqual({ value: 'i18n/id:something' })
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('toJsonStorable() handles smartloc with countable translate', () => {
|
|
43
|
-
const str = { value: loc.plural(42)('something')`Something ${42}` };
|
|
44
|
-
expect(toJsonStorable(str, { nonSelfDescriptive: 'id' })).toEqual({ value: {
|
|
45
|
-
i18n: 'something',
|
|
46
|
-
data: [42],
|
|
47
|
-
count: 42
|
|
48
|
-
} })
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
it('toJsonStorable() handles smartloc with inclusions', () => {
|
|
53
|
-
const str = { value: loc('something')`Answer: ${42} with ${'str'}` };
|
|
54
|
-
expect(toJsonStorable(str, { nonSelfDescriptive: 'id' })).toEqual({
|
|
55
|
-
value: {
|
|
56
|
-
i18n: 'something',
|
|
57
|
-
data: [42, 'str']
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('toJsonStorable() handles smartloc with translate option to "toMulti"', () => {
|
|
63
|
-
const str = { value: loc('something')`Something` };
|
|
64
|
-
addLocale('fr', { something: 'Quelque chose' })
|
|
65
|
-
expect(toJsonStorable(str, { nonSelfDescriptive: 'toMulti' })).toEqual({ value: { 'i18n:fr': 'Quelque chose', 'i18n:en-US': 'Something', } })
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
it('toJsonStorable() handles smartloc with translate option to "skip/"', () => {
|
|
70
|
-
const str = { value: loc('something')`Something` };
|
|
71
|
-
addLocale('fr', { something: 'Quelque chose' })
|
|
72
|
-
const got = toJsonStorable(str, { nonSelfDescriptive: 'skip' });
|
|
73
|
-
expect(got).toEqual(str);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('can parse a multiloc', () => {
|
|
77
|
-
const str = { value: new MultiLoc({ fr: 'FR value', en: 'EN value' }) };
|
|
78
|
-
const serial = withSerializationContext(() => JSON.stringify(str));
|
|
79
|
-
const deser = jsonParseLocalized(serial);
|
|
80
|
-
expect(deser.value).toBeInstanceOf(MultiLoc);
|
|
81
|
-
// check translation
|
|
82
|
-
addLocale('fr-FR', {});
|
|
83
|
-
const translated = withLocales(['fr'], () => JSON.stringify(deser));
|
|
84
|
-
expect(translated).toEqual('{"value":"FR value"}');
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
it('can parse a singleloc', () => {
|
|
89
|
-
const str = { value: [new SingleLoc(`first`), new SingleLoc(`second`)] };
|
|
90
|
-
const serial = withSerializationContext(() => JSON.stringify(str));
|
|
91
|
-
const deser = jsonParseLocalized(serial);
|
|
92
|
-
expect(deser.value).toBeInstanceOf(Array);
|
|
93
|
-
for (const k of deser.value) {
|
|
94
|
-
expect(k).toBeInstanceOf(SingleLoc);
|
|
95
|
-
}
|
|
96
|
-
// check translation
|
|
97
|
-
addLocale('fr-FR', {});
|
|
98
|
-
const translated = withLocales(['fr'], () => JSON.stringify(deser));
|
|
99
|
-
expect(translated).toEqual('{"value":["first","second"]}');
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
it('can parse a smartloc', () => {
|
|
104
|
-
// must be registered to deserialize ids
|
|
105
|
-
addLocale('fr', { something: 'Quelque chose' });
|
|
106
|
-
addLocale('en-US', { something: 'Something' });
|
|
107
|
-
|
|
108
|
-
const str = { value: [loc('something')`Something`] };
|
|
109
|
-
const serial = withSerializationContext(() => JSON.stringify(str));
|
|
110
|
-
const deser = jsonParseLocalized(serial);
|
|
111
|
-
expect(deser.value).toBeInstanceOf(Array);
|
|
112
|
-
for (const k of deser.value) {
|
|
113
|
-
expect(k).toBeInstanceOf(SmartLoc);
|
|
114
|
-
}
|
|
115
|
-
// check translation
|
|
116
|
-
let translated = withLocales(['fr'], () => JSON.stringify(deser));
|
|
117
|
-
expect(translated).toEqual('{"value":["Quelque chose"]}');
|
|
118
|
-
translated = withLocales(['en'], () => JSON.stringify(deser));
|
|
119
|
-
expect(translated).toEqual('{"value":["Something"]}');
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
it('can parse a smartloc with arguments', () => {
|
|
124
|
-
// must be registered to deserialize ids
|
|
125
|
-
addLocale('fr', { something: 'Quelque chose {0} {1}' });
|
|
126
|
-
addLocale('en-US', { something: 'Something {0} {1}' });
|
|
127
|
-
|
|
128
|
-
const str = { value: [loc('something')`Something ${'str'} ${42}`] };
|
|
129
|
-
const serial = withSerializationContext(() => JSON.stringify(str));
|
|
130
|
-
const deser = jsonParseLocalized(serial);
|
|
131
|
-
expect(deser.value).toBeInstanceOf(Array);
|
|
132
|
-
for (const k of deser.value) {
|
|
133
|
-
expect(k).toBeInstanceOf(SmartLoc);
|
|
134
|
-
}
|
|
135
|
-
// check translation
|
|
136
|
-
let translated = withLocales(['fr'], () => JSON.stringify(deser));
|
|
137
|
-
expect(translated).toEqual('{"value":["Quelque chose str 42"]}');
|
|
138
|
-
translated = withLocales(['en'], () => JSON.stringify(deser));
|
|
139
|
-
expect(translated).toEqual('{"value":["Something str 42"]}');
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
it('should return the same object instance if no smartloc instance', async () => {
|
|
144
|
-
const obj = {
|
|
145
|
-
a: { b: [42, { c: 51 }] }
|
|
146
|
-
};
|
|
147
|
-
const ret = toJsonStorable(obj);
|
|
148
|
-
expect(obj === ret).toBe(true);
|
|
149
|
-
const deser = toLocalizable(ret);
|
|
150
|
-
expect(obj === deser).toBe(true);
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('should return other instance if no smartloc instance', async () => {
|
|
154
|
-
const obj = {
|
|
155
|
-
a: { b: [42, { c: loc`Whatever` }] }
|
|
156
|
-
};
|
|
157
|
-
const ret = toJsonStorable(obj);
|
|
158
|
-
expect(obj === ret).toBe(false);
|
|
159
|
-
const deser = toLocalizable(ret);
|
|
160
|
-
expect(ret === deser).toBe(false);
|
|
161
|
-
});
|
|
162
|
-
});
|