aboutsummaryrefslogtreecommitdiffstats
path: root/packages/main/src/i18n/impl/LocaltizationFiles.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/main/src/i18n/impl/LocaltizationFiles.ts')
-rw-r--r--packages/main/src/i18n/impl/LocaltizationFiles.ts93
1 files changed, 93 insertions, 0 deletions
diff --git a/packages/main/src/i18n/impl/LocaltizationFiles.ts b/packages/main/src/i18n/impl/LocaltizationFiles.ts
new file mode 100644
index 0000000..74e3ae3
--- /dev/null
+++ b/packages/main/src/i18n/impl/LocaltizationFiles.ts
@@ -0,0 +1,93 @@
1/*
2 * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21import { readFile, writeFile } from 'node:fs/promises';
22
23import type { ResourceKey } from 'i18next';
24
25import type Resources from '../../infrastructure/resources/Resources.js';
26import getLogger from '../../utils/getLogger.js';
27import isErrno from '../../utils/isErrno.js';
28import type LocatlizationRepository from '../LocalizationRepository.js';
29
30const log = getLogger('LocalizationFiles');
31
32export default class LocalizationFiles implements LocatlizationRepository {
33 constructor(private readonly resources: Resources) {}
34
35 async getResourceContents(
36 language: string,
37 namespace: string,
38 ): Promise<ResourceKey> {
39 const fileName = this.resources.getLocalizationPath(
40 language,
41 `${namespace}.json`,
42 );
43 const contents = await readFile(fileName, 'utf8');
44 log.info('Read localization file', fileName);
45 // The contents of the file should come from a signed archive during production,
46 // so we don't need to (and can't) validate them.
47 return JSON.parse(contents) as ResourceKey;
48 }
49
50 private getMissingLocalizationPath(
51 language: string,
52 namespace: string,
53 ): string {
54 return this.resources.getLocalizationPath(
55 language,
56 `${namespace}.missing.json`,
57 );
58 }
59
60 async getMissingResourceContents(
61 language: string,
62 namespace: string,
63 ): Promise<ResourceKey> {
64 const fileName = this.getMissingLocalizationPath(language, namespace);
65 try {
66 const contents = await readFile(fileName, 'utf8');
67 // The contents of the file are only used during development,
68 // so we don't need to (and can't) validate them.
69 return JSON.parse(contents) as ResourceKey;
70 } catch (error) {
71 if (isErrno(error, 'ENOENT')) {
72 log.debug(
73 'No missing translations file',
74 language,
75 'for namespace',
76 namespace,
77 );
78 return {};
79 }
80 throw error;
81 }
82 }
83
84 setMissingResourceContents(
85 language: string,
86 namespace: string,
87 data: ResourceKey,
88 ): Promise<void> {
89 const fileName = this.getMissingLocalizationPath(language, namespace);
90 const contents = JSON.stringify(data, undefined, 2);
91 return writeFile(fileName, contents, 'utf8');
92 }
93}