lightnet 4.7.0 → 4.7.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # lightnet
2
2
 
3
+ ## 4.7.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#469](https://github.com/LightNetDev/LightNet/pull/469) [`a0961c4`](https://github.com/LightNetDev/LightNet/commit/a0961c40fdc30a93d3d5c814343f9c12ec26b2ef) - Warn about user-defined ln.* translation overrides that no longer match a built-in English key. Include source filenames and check unused keys without failing check-translations for stale overrides alone.
8
+
9
+ - [#469](https://github.com/LightNetDev/LightNet/pull/469) [`a0961c4`](https://github.com/LightNetDev/LightNet/commit/a0961c40fdc30a93d3d5c814343f9c12ec26b2ef) - Only record translation keys requested during the build, so check-translations no longer reports missing translations for unused built-in or user keys.
10
+
3
11
  ## 4.7.0
4
12
 
5
13
  ### Minor Changes
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "LightNet makes it easy to run your own digital media library.",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
- "version": "4.7.0",
6
+ "version": "4.7.1",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "https://github.com/LightNetDev/lightnet",
@@ -25,6 +25,20 @@ const recordedTranslations = new Set<string>()
25
25
 
26
26
  const translationStore = lazy(() => createTranslationStore())
27
27
 
28
+ export async function writeStaleTranslationOverrides(
29
+ overrides: { key: string; path: string }[],
30
+ ) {
31
+ if (!import.meta.env.PROD) {
32
+ return
33
+ }
34
+ await mkdir(lightnetCachePath, { recursive: true })
35
+ await writeFile(
36
+ resolve(lightnetCachePath, "stale-translation-overrides.json"),
37
+ JSON.stringify(overrides),
38
+ "utf-8",
39
+ )
40
+ }
41
+
28
42
  const writeLanguagesManifest = async () => {
29
43
  const manifestPath = resolve(lightnetCachePath, "languages.json")
30
44
  const { defaultLocale, locales } = config
@@ -68,7 +68,6 @@ export async function useTranslate(
68
68
  const resolvedLocale = bcp47 ?? config.defaultLocale
69
69
  const translations = await i18nextTranslations.get()
70
70
  const availableTranslationKeys = new Set(await translationKeys.get())
71
- recordAllTranslations()
72
71
 
73
72
  const i18n = i18next.createInstance()
74
73
 
@@ -87,6 +86,19 @@ export async function useTranslate(
87
86
  "en",
88
87
  ]
89
88
  return (input, options) => {
89
+ if (import.meta.env.PROD) {
90
+ recordTranslation({
91
+ key: input,
92
+ type: input.startsWith("ln.") ? "lightnet" : "user",
93
+ values: Object.fromEntries(
94
+ config.locales.map((locale) => [
95
+ locale,
96
+ translations[locale]?.translation[input],
97
+ ]),
98
+ ),
99
+ })
100
+ }
101
+
90
102
  const t = i18n.getFixedT(resolvedLocale) as (
91
103
  key: TranslationKey,
92
104
  options?: TOptions,
@@ -101,39 +113,3 @@ export async function useTranslate(
101
113
  return value
102
114
  }
103
115
  }
104
-
105
- async function recordAllTranslations() {
106
- if (!import.meta.env.PROD) {
107
- // only record translations during build time
108
- return
109
- }
110
-
111
- const { locales } = config
112
- const localesIncludingEnglish = [...locales.filter((l) => l !== "en"), "en"]
113
- const keys = await translationKeys.get()
114
- const translations = await i18nextTranslations.get()
115
-
116
- const collectValues = (key: string, locales: string[]) => {
117
- const values = {} as Record<string, string | undefined>
118
- for (const locale of locales) {
119
- values[locale] = translations[locale]?.translation[key]
120
- }
121
- return values
122
- }
123
-
124
- for (const key of keys) {
125
- if (key.startsWith("ln.")) {
126
- recordTranslation({
127
- key,
128
- values: collectValues(key, localesIncludingEnglish),
129
- type: "lightnet",
130
- })
131
- } else {
132
- recordTranslation({
133
- key,
134
- values: collectValues(key, locales),
135
- type: "user",
136
- })
137
- }
138
- }
139
- }
@@ -1,6 +1,9 @@
1
1
  import { z } from "astro/zod"
2
2
  import YAML from "yaml"
3
3
 
4
+ import { lazy } from "../utils/lazy"
5
+ import { writeStaleTranslationOverrides } from "./record-translation"
6
+
4
7
  const builtInTranslations = {
5
8
  ar: () => import("./translations/ar.yml?raw"),
6
9
  bn: () => import("./translations/bn.yml?raw"),
@@ -22,24 +25,52 @@ type BuiltInLanguage = keyof typeof builtInTranslations
22
25
 
23
26
  const translationFileSchema = z.record(z.string(), z.string())
24
27
 
28
+ const userTranslationFiles = import.meta.glob<string>(
29
+ ["/src/translations/*.(yml|yaml)"],
30
+ { query: "?raw", import: "default" },
31
+ )
32
+
25
33
  const userTranslations = Object.fromEntries(
26
- Object.entries(
27
- import.meta.glob(["/src/translations/*.(yml|yaml)"], {
28
- query: "?raw",
29
- import: "default",
30
- }),
31
- ).map(([path, translationImport]) => {
34
+ Object.entries(userTranslationFiles).map(([path, translationImport]) => {
32
35
  const [fileName] = path.split("/").slice(-1)
33
36
  const lang = fileName.replace(/\.ya?ml/, "")
34
37
  return [lang, translationImport]
35
38
  }),
36
39
  )
37
40
 
38
- export const loadTranslations = async (bcp47: string) => ({
39
- ...(await loadBuiltInTranslations(builtInTranslations, bcp47)),
40
- ...(await loadUserTranslations(bcp47)),
41
+ const staleOverrides = lazy(async () => {
42
+ const english = await loadBuiltInTranslations(builtInTranslations, "en")
43
+ await writeStaleTranslationOverrides(
44
+ await findStaleTranslationOverrides(english, userTranslationFiles),
45
+ )
41
46
  })
42
47
 
48
+ export async function findStaleTranslationOverrides(
49
+ english: Record<string, string>,
50
+ files: Record<string, () => Promise<string>>,
51
+ ) {
52
+ const overrides: { key: string; path: string }[] = []
53
+ for (const [path, load] of Object.entries(files)) {
54
+ const translations = translationFileSchema.parse(YAML.parse(await load()))
55
+ for (const key of Object.keys(translations)) {
56
+ if (key.startsWith("ln.") && !Object.hasOwn(english, key)) {
57
+ overrides.push({ key, path: path.replace(/^\//, "") })
58
+ }
59
+ }
60
+ }
61
+ return overrides
62
+ }
63
+
64
+ export const loadTranslations = async (bcp47: string) => {
65
+ if (import.meta.env.PROD) {
66
+ await staleOverrides.get()
67
+ }
68
+ return {
69
+ ...(await loadBuiltInTranslations(builtInTranslations, bcp47)),
70
+ ...(await loadUserTranslations(bcp47)),
71
+ }
72
+ }
73
+
43
74
  function hasTranslations(
44
75
  translationMap: Record<string, unknown>,
45
76
  bcp47: string,