@awsless/i18n 0.0.14 → 0.0.16

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/README.MD CHANGED
@@ -57,9 +57,9 @@ import { lang } from '@awsless/i18n/svelte'
57
57
 
58
58
  const count = 1
59
59
 
60
- lang.t.get(`${count} count`, {
61
- es: `${count} contar`,
62
- jp: `${count} カウント`,
60
+ lang.t.get(lang.s`${count} count`, {
61
+ es: lang.s`${count} contar`,
62
+ jp: lang.s`${count} カウント`,
63
63
  })
64
64
  ```
65
65
 
@@ -73,15 +73,15 @@ lang.locale = 'jp'
73
73
 
74
74
  ## Changing the AI-generated text
75
75
 
76
- A `i18n.json` file with all the translations will be generated in the root of your project the first time you run a build. We use this file as a cache to not translate any text that has already been translated before.
76
+ A `i18n.generated.json` file with the auto-generated translations will be created in the root of your project the first time you run a build. We use this file as a cache to not translate any text that has already been translated before.
77
77
 
78
- If you don't like the auto-generated translations by AI, you can safely change any of the ai generated translations here.
78
+ If you want to overwrite an AI-generated translation, create or edit `i18n.json`. Any translation in `i18n.json` takes precedence over the generated cache during build time.
79
79
 
80
80
  ```json
81
81
  {
82
- "${count} count": {
83
- "es": "${count} contar",
84
- "jp": "${count} カウント"
85
- }
82
+ "${count} count": {
83
+ "es": "${count} contar",
84
+ "jp": "${count} カウント"
85
+ }
86
86
  }
87
87
  ```
package/dist/index.js CHANGED
@@ -4,8 +4,10 @@ import MagicString from "magic-string";
4
4
  // src/cache.ts
5
5
  import { readFile, stat, writeFile } from "fs/promises";
6
6
  import { join } from "path";
7
- var loadCache = async (cwd) => {
8
- const file = join(cwd, "i18n.json");
7
+ var GENERATED_CACHE_FILE = "i18n.generated.json";
8
+ var OVERRIDE_CACHE_FILE = "i18n.json";
9
+ var loadFile = async (cwd, fileName) => {
10
+ const file = join(cwd, fileName);
9
11
  try {
10
12
  await stat(file);
11
13
  } catch (error) {
@@ -14,13 +16,29 @@ var loadCache = async (cwd) => {
14
16
  const data = await readFile(file, "utf8");
15
17
  return new Cache(JSON.parse(data));
16
18
  };
19
+ var loadGeneratedCache = async (cwd) => {
20
+ return loadFile(cwd, GENERATED_CACHE_FILE);
21
+ };
22
+ var loadOverrideCache = async (cwd) => {
23
+ return loadFile(cwd, OVERRIDE_CACHE_FILE);
24
+ };
17
25
  var saveCache = async (cwd, cache) => {
18
- await writeFile(join(cwd, "i18n.json"), JSON.stringify(cache, void 0, 2));
26
+ await writeFile(join(cwd, GENERATED_CACHE_FILE), JSON.stringify(cache.toJSON(), void 0, 2) + "\n");
27
+ };
28
+ var mergeCaches = (...caches) => {
29
+ const merged = new Cache();
30
+ for (const cache of caches) {
31
+ for (const item of cache.entries()) {
32
+ merged.replace(item.source, item.locale, item.translation);
33
+ }
34
+ }
35
+ return merged;
19
36
  };
20
37
  var Cache = class {
21
38
  constructor(data = {}) {
22
39
  this.data = data;
23
40
  }
41
+ data;
24
42
  set(source, locale, translation) {
25
43
  if (!this.data[source]) {
26
44
  this.data[source] = {};
@@ -29,6 +47,12 @@ var Cache = class {
29
47
  this.data[source][locale] = translation;
30
48
  }
31
49
  }
50
+ replace(source, locale, translation) {
51
+ if (!this.data[source]) {
52
+ this.data[source] = {};
53
+ }
54
+ this.data[source][locale] = translation;
55
+ }
32
56
  get(source, locale) {
33
57
  return this.data[source]?.[locale];
34
58
  }
@@ -51,7 +75,16 @@ var Cache = class {
51
75
  }
52
76
  }
53
77
  toJSON() {
54
- return this.data;
78
+ return Object.fromEntries(
79
+ Object.entries(this.data).sort(([left], [right]) => left.localeCompare(right)).map(([source, locales]) => {
80
+ return [
81
+ source,
82
+ Object.fromEntries(
83
+ Object.entries(locales).sort(([left], [right]) => left.localeCompare(right))
84
+ )
85
+ ];
86
+ })
87
+ );
55
88
  }
56
89
  };
57
90
 
@@ -116,16 +149,15 @@ import { parse } from "@swc/core";
116
149
  import { simple } from "swc-walk";
117
150
  var findTypescriptTranslatable = async (code) => {
118
151
  const found = [];
119
- const ast = await parse(code, {
120
- syntax: "typescript"
121
- });
152
+ const ast = await parse(code, { syntax: "typescript" });
153
+ const bytes = Buffer.from(code, "utf8");
122
154
  simple(ast, {
123
155
  TaggedTemplateExpression(node) {
124
156
  if (node.tag.type === "MemberExpression" && node.tag.object.type === "Identifier" && node.tag.object.value === "lang" && node.tag.property.type === "Identifier" && node.tag.property.value === "t") {
125
- const content = code.substring(
157
+ const content = bytes.subarray(
126
158
  node.template.span.start - ast.span.start + 1,
127
159
  node.template.span.end - ast.span.start - 1
128
- );
160
+ ).toString("utf8");
129
161
  found.push(content);
130
162
  }
131
163
  }
@@ -162,6 +194,7 @@ var findTranslatable = async (cwd) => {
162
194
  // src/vite.ts
163
195
  var i18n = (props) => {
164
196
  let cache;
197
+ let generatedCache;
165
198
  return {
166
199
  name: "awsless/i18n",
167
200
  enforce: "pre",
@@ -169,18 +202,21 @@ var i18n = (props) => {
169
202
  const cwd = process.cwd();
170
203
  this.info("Finding all translatable text...");
171
204
  const sourceTexts = await findTranslatable(cwd);
172
- cache = await loadCache(cwd);
173
- removeUnusedTranslations(cache, sourceTexts, props.locales);
205
+ generatedCache = await loadGeneratedCache(cwd);
206
+ const overrideCache = await loadOverrideCache(cwd);
207
+ removeUnusedTranslations(generatedCache, sourceTexts, props.locales);
208
+ cache = mergeCaches(generatedCache, overrideCache);
174
209
  const newSourceTexts = findNewTranslations(cache, sourceTexts, props.locales);
175
210
  if (newSourceTexts.length > 0) {
176
211
  this.info(`Translating ${newSourceTexts.length} new texts.`);
177
212
  const translations = await props.translate(props.default ?? "en", newSourceTexts);
178
213
  this.info(`Translated ${translations.length} texts.`);
179
214
  for (const item of translations) {
180
- cache.set(item.source, item.locale, item.translation);
215
+ generatedCache.set(item.source, item.locale, item.translation);
181
216
  }
182
217
  }
183
- await saveCache(cwd, cache);
218
+ cache = mergeCaches(generatedCache, overrideCache);
219
+ await saveCache(cwd, generatedCache);
184
220
  this.info(`Translating done.`);
185
221
  },
186
222
  transform(code) {
@@ -1,3 +1,9 @@
1
+ type StringArgs = Array<string | number | {
2
+ toString(): string;
3
+ }>;
4
+ type Translate = {
5
+ (template: TemplateStringsArray, ...args: StringArgs): string;
6
+ };
1
7
  declare const lang: {
2
8
  /** Get the current locale.
3
9
  *
@@ -12,9 +18,7 @@ declare const lang: {
12
18
  * @example
13
19
  * lang.t`Hello world!`
14
20
  */
15
- readonly t: (template: TemplateStringsArray, ...args: Array<string | number | {
16
- toString(): string;
17
- }>) => string;
21
+ readonly t: Translate;
18
22
  };
19
23
 
20
24
  export { lang };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awsless/i18n",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -42,12 +42,12 @@
42
42
  "devDependencies": {
43
43
  "@ai-sdk/openai": "^2.0.42",
44
44
  "@sveltejs/vite-plugin-svelte": "^3.1.2",
45
- "svelte": "^4.2.19",
45
+ "svelte": "^5.46.0",
46
46
  "svelte-preprocess": "^6.0.2",
47
47
  "vite": "^7.1.9"
48
48
  },
49
49
  "peerDependencies": {
50
- "svelte": "^4.2.19",
50
+ "svelte": "^5.46.0",
51
51
  "vite": "^7.1.9"
52
52
  },
53
53
  "dependencies": {