mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-14 10:34:06 +02:00
Convert V2 translations to Toml
This commit is contained in:
Generated
+13
-61
@@ -53,7 +53,6 @@
|
||||
"globals": "^16.4.0",
|
||||
"i18next": "^25.5.2",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"jszip": "^3.10.1",
|
||||
"license-report": "^6.8.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
@@ -65,6 +64,7 @@
|
||||
"react-rnd": "^10.5.2",
|
||||
"react-router-dom": "^7.9.1",
|
||||
"signature_pad": "^5.0.4",
|
||||
"smol-toml": "^1.4.2",
|
||||
"tailwindcss": "^4.1.13",
|
||||
"web-vitals": "^5.1.0"
|
||||
},
|
||||
@@ -6453,15 +6453,6 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
||||
"integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.6.12"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -8893,15 +8884,6 @@
|
||||
"@babel/runtime": "^7.23.2"
|
||||
}
|
||||
},
|
||||
"node_modules/i18next-http-backend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.2.tgz",
|
||||
"integrity": "sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cross-fetch": "4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
@@ -10717,48 +10699,6 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/node-fetch/node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.27",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
|
||||
@@ -13111,6 +13051,18 @@
|
||||
"npm": ">= 3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/smol-toml": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.2.tgz",
|
||||
"integrity": "sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/cyyynthia"
|
||||
}
|
||||
},
|
||||
"node_modules/socks": {
|
||||
"version": "2.8.7",
|
||||
"resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
"globals": "^16.4.0",
|
||||
"i18next": "^25.5.2",
|
||||
"i18next-browser-languagedetector": "^8.2.0",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
"jszip": "^3.10.1",
|
||||
"license-report": "^6.8.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
@@ -61,6 +60,7 @@
|
||||
"react-rnd": "^10.5.2",
|
||||
"react-router-dom": "^7.9.1",
|
||||
"signature_pad": "^5.0.4",
|
||||
"smol-toml": "^1.4.2",
|
||||
"tailwindcss": "^4.1.13",
|
||||
"web-vitals": "^5.1.0"
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
import Backend from 'i18next-http-backend';
|
||||
import TomlBackend from '@app/i18n/tomlBackend';
|
||||
|
||||
// Define supported languages (based on your existing translations)
|
||||
export const supportedLanguages = {
|
||||
@@ -51,7 +51,7 @@ export const supportedLanguages = {
|
||||
export const rtlLanguages = ['ar-AR', 'fa-IR'];
|
||||
|
||||
i18n
|
||||
.use(Backend)
|
||||
.use(TomlBackend)
|
||||
.use(LanguageDetector)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
@@ -73,7 +73,7 @@ i18n
|
||||
const lng = lngs[0];
|
||||
const basePath = import.meta.env.BASE_URL || '/';
|
||||
const cleanBasePath = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath;
|
||||
return `${cleanBasePath}/locales/${lng}/${namespaces[0]}.json`;
|
||||
return `${cleanBasePath}/locales/${lng}/${namespaces[0]}.toml`;
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import Backend from 'i18next-http-backend';
|
||||
import TomlBackend from '@app/i18n/tomlBackend';
|
||||
|
||||
i18n
|
||||
.use(Backend)
|
||||
.use(TomlBackend)
|
||||
.use(initReactI18next)
|
||||
.init({
|
||||
lng: 'en',
|
||||
fallbackLng: 'en',
|
||||
debug: false,
|
||||
backend: {
|
||||
loadPath: '/locales/{{lng}}/{{ns}}.json',
|
||||
loadPath: '/locales/{{lng}}/{{ns}}.toml',
|
||||
},
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { BackendModule, ReadCallback } from 'i18next';
|
||||
import { parse } from 'smol-toml';
|
||||
|
||||
export interface TomlBackendOptions {
|
||||
loadPath: string | ((lngs: string[], namespaces: string[]) => string);
|
||||
}
|
||||
|
||||
class TomlBackend implements BackendModule<TomlBackendOptions> {
|
||||
static type = 'backend' as const;
|
||||
type = 'backend' as const;
|
||||
|
||||
constructor(services?: unknown, options?: TomlBackendOptions) {
|
||||
this.init(services, options);
|
||||
}
|
||||
|
||||
init(_services?: unknown, options?: TomlBackendOptions): void {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
read(language: string, namespace: string, callback: ReadCallback): void {
|
||||
const loadPath = this.options?.loadPath;
|
||||
|
||||
if (!loadPath) {
|
||||
callback(new Error('loadPath is not configured'), null);
|
||||
return;
|
||||
}
|
||||
|
||||
const url = typeof loadPath === 'function'
|
||||
? loadPath([language], [namespace])
|
||||
: loadPath.replace('{{lng}}', language).replace('{{ns}}', namespace);
|
||||
|
||||
fetch(url)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load translation file: ${url} (${response.status})`);
|
||||
}
|
||||
return response.text();
|
||||
})
|
||||
.then((tomlContent) => {
|
||||
const parsed = parse(tomlContent);
|
||||
callback(null, parsed);
|
||||
})
|
||||
.catch((error) => {
|
||||
callback(error, null);
|
||||
});
|
||||
}
|
||||
|
||||
private options?: TomlBackendOptions;
|
||||
}
|
||||
|
||||
export default TomlBackend;
|
||||
@@ -2,10 +2,11 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
import ts from 'typescript';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { parse } from 'smol-toml';
|
||||
|
||||
const REPO_ROOT = path.join(__dirname, '../../../..');
|
||||
const SRC_ROOT = path.join(__dirname, '../..');
|
||||
const EN_GB_FILE = path.join(__dirname, '../../../public/locales/en-GB/translation.json');
|
||||
const EN_GB_FILE = path.join(__dirname, '../../../public/locales/en-GB/translation.toml');
|
||||
|
||||
const IGNORED_DIRS = new Set([
|
||||
'tests',
|
||||
@@ -150,7 +151,7 @@ describe('Missing translation coverage', () => {
|
||||
expect(fs.existsSync(EN_GB_FILE)).toBe(true);
|
||||
|
||||
const localeContent = fs.readFileSync(EN_GB_FILE, 'utf8');
|
||||
const enGb = JSON.parse(localeContent);
|
||||
const enGb = parse(localeContent);
|
||||
const availableKeys = flattenKeys(enGb);
|
||||
|
||||
const usedKeys = listSourceFiles()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { parse } from 'smol-toml';
|
||||
|
||||
const LOCALES_DIR = path.join(__dirname, '../../../public/locales');
|
||||
|
||||
@@ -17,7 +18,7 @@ const getLocaleDirectories = () => {
|
||||
|
||||
const localeDirectories = getLocaleDirectories();
|
||||
|
||||
describe('Translation JSON Validation', () => {
|
||||
describe('Translation TOML Validation', () => {
|
||||
test('should find the locales directory', () => {
|
||||
expect(fs.existsSync(LOCALES_DIR)).toBe(true);
|
||||
});
|
||||
@@ -26,8 +27,8 @@ describe('Translation JSON Validation', () => {
|
||||
expect(localeDirectories.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test.each(localeDirectories)('should have valid JSON in %s/translation.json', (localeDir) => {
|
||||
const translationFile = path.join(LOCALES_DIR, localeDir, 'translation.json');
|
||||
test.each(localeDirectories)('should have valid TOML in %s/translation.toml', (localeDir) => {
|
||||
const translationFile = path.join(LOCALES_DIR, localeDir, 'translation.toml');
|
||||
|
||||
// Check if file exists
|
||||
expect(fs.existsSync(translationFile)).toBe(true);
|
||||
@@ -36,15 +37,15 @@ describe('Translation JSON Validation', () => {
|
||||
const content = fs.readFileSync(translationFile, 'utf8');
|
||||
expect(content.trim()).not.toBe('');
|
||||
|
||||
// Parse JSON - this will throw if invalid JSON
|
||||
let jsonData;
|
||||
// Parse TOML - this will throw if invalid TOML
|
||||
let tomlData;
|
||||
expect(() => {
|
||||
jsonData = JSON.parse(content);
|
||||
tomlData = parse(content);
|
||||
}).not.toThrow();
|
||||
|
||||
// Ensure it's an object at root level
|
||||
expect(typeof jsonData).toBe('object');
|
||||
expect(jsonData).not.toBeNull();
|
||||
expect(Array.isArray(jsonData)).toBe(false);
|
||||
expect(typeof tomlData).toBe('object');
|
||||
expect(tomlData).not.toBeNull();
|
||||
expect(Array.isArray(tomlData)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { parse } from 'smol-toml';
|
||||
|
||||
const LOCALES_DIR = path.join(__dirname, '../../../public/locales');
|
||||
|
||||
@@ -40,11 +41,11 @@ describe('Translation key structure', () => {
|
||||
expect(localeDirectories.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test.each(localeDirectories)('should not contain dotted keys in %s/translation.json', (localeDir) => {
|
||||
const translationFile = path.join(LOCALES_DIR, localeDir, 'translation.json');
|
||||
test.each(localeDirectories)('should not contain dotted keys in %s/translation.toml', (localeDir) => {
|
||||
const translationFile = path.join(LOCALES_DIR, localeDir, 'translation.toml');
|
||||
expect(fs.existsSync(translationFile)).toBe(true);
|
||||
|
||||
const data = JSON.parse(fs.readFileSync(translationFile, 'utf8'));
|
||||
const data = parse(fs.readFileSync(translationFile, 'utf8'));
|
||||
const dottedKeys = findDottedKeys(data);
|
||||
expect(dottedKeys, `Dotted keys found in ${localeDir}: ${dottedKeys.join(', ')}`).toHaveLength(0);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user