mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2026-03-13 02:18:16 +01:00
Convert V2 translations to Toml
This commit is contained in:
@@ -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,
|
||||
@@ -29,7 +29,7 @@ i18n
|
||||
'convert.singleOrMultiple': 'Output',
|
||||
'convert.emailNote': 'Email attachments and embedded images will be included',
|
||||
'common.color': 'Color',
|
||||
'common.grayscale': 'Grayscale',
|
||||
'common.grayscale': 'Grayscale',
|
||||
'common.blackWhite': 'Black & White',
|
||||
'common.single': 'Single Image',
|
||||
'common.multiple': 'Multiple Images',
|
||||
@@ -45,4 +45,4 @@ i18n
|
||||
}
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
export default i18n;
|
||||
|
||||
51
frontend/src/core/i18n/tomlBackend.ts
Normal file
51
frontend/src/core/i18n/tomlBackend.ts
Normal file
@@ -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