smartloc 2.0.2 → 2.0.4

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.
Files changed (100) hide show
  1. package/adapters/adapter-base.d.ts +12 -0
  2. package/adapters/adapter-base.js +56 -0
  3. package/adapters/adapter-base.js.map +1 -0
  4. package/adapters/index.d.ts +4 -0
  5. package/adapters/index.js +32 -0
  6. package/adapters/index.js.map +1 -0
  7. package/adapters/json.d.ts +6 -0
  8. package/adapters/json.js +23 -0
  9. package/adapters/json.js.map +1 -0
  10. package/adapters/xliff.d.ts +6 -0
  11. package/adapters/xliff.js +35 -0
  12. package/adapters/xliff.js.map +1 -0
  13. package/cli/cli.d.ts +2 -0
  14. package/cli/cli.js +129 -0
  15. package/cli/cli.js.map +1 -0
  16. package/cli/collect.d.ts +14 -0
  17. package/{src/cli/collect.ts → cli/collect.js} +59 -75
  18. package/cli/collect.js.map +1 -0
  19. package/cli/interfaces.js +3 -0
  20. package/cli/interfaces.js.map +1 -0
  21. package/cli/reconciliate.d.ts +6 -0
  22. package/{src/cli/reconciliate.ts → cli/reconciliate.js} +35 -40
  23. package/cli/reconciliate.js.map +1 -0
  24. package/cli/utils.d.ts +6 -0
  25. package/{src/cli/utils.ts → cli/utils.js} +16 -12
  26. package/cli/utils.js.map +1 -0
  27. package/core/default-locale.d.ts +10 -0
  28. package/{src/core/default-locale.tsx → core/default-locale.js} +23 -28
  29. package/core/default-locale.js.map +1 -0
  30. package/{src/core/interfaces.ts → core/interfaces.d.ts} +15 -20
  31. package/core/interfaces.js +3 -0
  32. package/core/interfaces.js.map +1 -0
  33. package/core/json-utils.d.ts +19 -0
  34. package/{src/core/json-utils.ts → core/json-utils.js} +27 -33
  35. package/core/json-utils.js.map +1 -0
  36. package/core/literal.d.ts +13 -0
  37. package/{src/core/literal.ts → core/literal.js} +20 -19
  38. package/core/literal.js.map +1 -0
  39. package/core/load.d.ts +32 -0
  40. package/core/load.js +50 -0
  41. package/core/load.js.map +1 -0
  42. package/core/locale-list.d.ts +27 -0
  43. package/{src/core/locale-list.ts → core/locale-list.js} +35 -38
  44. package/core/locale-list.js.map +1 -0
  45. package/core/locale.d.ts +15 -0
  46. package/{src/core/locale.tsx → core/locale.js} +29 -28
  47. package/core/locale.js.map +1 -0
  48. package/core/smartloc.d.ts +39 -0
  49. package/{src/core/smartloc.tsx → core/smartloc.js} +104 -122
  50. package/core/smartloc.js.map +1 -0
  51. package/core/tag.d.ts +13 -0
  52. package/core/tag.js +26 -0
  53. package/core/tag.js.map +1 -0
  54. package/core/utils.d.ts +4 -0
  55. package/core/utils.js +30 -0
  56. package/core/utils.js.map +1 -0
  57. package/express.d.ts +12 -0
  58. package/{src/express.ts → express.js} +33 -41
  59. package/express.js.map +1 -0
  60. package/graphql.d.ts +13 -0
  61. package/{src/graphql.ts → graphql.js} +62 -74
  62. package/graphql.js.map +1 -0
  63. package/index.js +30 -1
  64. package/index.js.map +1 -0
  65. package/node/index.d.ts +1 -0
  66. package/node/index.js +18 -0
  67. package/node/index.js.map +1 -0
  68. package/node/load.d.ts +12 -0
  69. package/node/load.js +37 -0
  70. package/node/load.js.map +1 -0
  71. package/package.json +3 -22
  72. package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +0 -111
  73. package/.vscode/launch.json +0 -65
  74. package/.vscode/settings.json +0 -14
  75. package/.vscode/tasks.json +0 -27
  76. package/build/README.md +0 -225
  77. package/build/package.json +0 -60
  78. package/bun.lock +0 -310
  79. package/samples/express/main.ts +0 -37
  80. package/samples/express/package.json +0 -10
  81. package/samples/graphql/main.ts +0 -73
  82. package/samples/graphql/package.json +0 -15
  83. package/src/adapters/adapter-base.ts +0 -58
  84. package/src/adapters/index.ts +0 -31
  85. package/src/adapters/json.ts +0 -18
  86. package/src/adapters/xliff.ts +0 -30
  87. package/src/cli/cli.ts +0 -137
  88. package/src/core/load.ts +0 -82
  89. package/src/core/tag.ts +0 -48
  90. package/src/core/utils.tsx +0 -24
  91. package/src/node/index.ts +0 -1
  92. package/src/node/load.ts +0 -32
  93. package/test/collect.spec.ts +0 -45
  94. package/test/reconciliation.spec.ts +0 -132
  95. package/test/serialization.spec.ts +0 -162
  96. package/test/translation.spec.ts +0 -61
  97. package/tsconfig.json +0 -37
  98. package/tslint.json +0 -103
  99. /package/{src/cli/interfaces.ts → cli/interfaces.d.ts} +0 -0
  100. /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;
@@ -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
- }
@@ -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
- });