Closed SciLor closed 1 month ago
not sure if this is something for the frontend.
As far as a know, you can show the missing translations if you were on a page where such missing translation appears, so if you do not visit every page in every variation, you will not get all missing translation strings.
another solution would be maintaining another file, which stores all translation keys and compare it to the actual ones. Also not really handy...
For now comparing the number of lines gives a good hint if something is missing (even there are currently some differences between all files in size of 1 line)
Can i create a page where all translations string are shown which are missing?
ChatGPT: Yes, you can create a page in your React app that shows all the missing translation keys using i18next. To do this, you can take advantage of i18next's missingKeyHandler or manually check for missing translations within your components.
Here’s how you can achieve that:
Approach 1: Using i18next missingKeyHandler You can track all the missing translation keys in the missingKeyHandler and store them in a state or a global variable. Then, display these missing keys on a separate page.
Step-by-Step Guide:
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
const missingTranslations = []; // Array to store missing keys
i18n
.use(initReactI18next) // Bind react-i18next to the instance
.init({
resources: {
en: {
translation: {
"welcome": "Welcome to the app!",
// Add your translations here
}
},
fr: {
translation: {
"welcome": "Bienvenue dans l'application!",
// Add your translations here
}
}
},
lng: 'en', // Default language
fallbackLng: 'en', // Fallback language if key is missing
debug: true, // Enable debug mode to track missing translations
saveMissing: true, // Enable saving missing translations
missingKeyHandler: (lng, ns, key, fallbackValue) => {
console.warn(`Missing translation for ${key} in ${lng}`);
if (!missingTranslations.includes(key)) {
missingTranslations.push(key); // Add missing key to the array
}
},
interpolation: {
escapeValue: false, // React already handles XSS
}
});
export { missingTranslations }; // Export the missing translations array
export default i18n;
import React from 'react';
import { missingTranslations } from './i18n';
const MissingTranslationsPage = () => {
return (
<div>
<h1>Missing Translations</h1>
{missingTranslations.length > 0 ? (
<ul>
{missingTranslations.map((key, index) => (
<li key={index}>{key}</li>
))}
</ul>
) : (
<p>All translations are available!</p>
)}
</div>
);
};
export default MissingTranslationsPage;
Code kopieren
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import MissingTranslationsPage from './MissingTranslationsPage';
import HomePage from './HomePage'; // Example of another page
const App = () => {
return (
<Router>
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/missing-translations" element={<MissingTranslationsPage />} />
</Routes>
</Router>
);
};
export default App;
Approach 2: Manually Check Missing Translations Alternatively, you can manually check for missing translations by iterating through the keys in your language files and comparing them. Here’s how to do that:
Code kopieren
export const translationKeys = [
'welcome',
'greeting',
'goodbye',
// Add all keys here
];
Code kopieren
import React from 'react';
import i18n from './i18n'; // Import your i18n configuration
import { translationKeys } from './translationKeys'; // Import all defined keys
const MissingTranslationsPage = () => {
const missingKeys = [];
// Check for missing translations in the current language
translationKeys.forEach((key) => {
if (!i18n.exists(key)) {
missingKeys.push(key);
}
});
return (
<div>
<h1>Missing Translations</h1>
{missingKeys.length > 0 ? (
<ul>
{missingKeys.map((key, index) => (
<li key={index}>{key}</li>
))}
</ul>
) : (
<p>All translations are available!</p>
)}
</div>
);
};
export default MissingTranslationsPage;
Explanation: Approach 1 dynamically logs any missing translation keys during runtime via missingKeyHandler. It is easy to implement, but you need to navigate through the app for it to detect all the missing keys.
Approach 2 compares all your translation keys at once against the existing translations. It requires you to maintain a list of all translation keys (translationKeys.js), but it can show all the missing keys at once without needing to interact with the app.
Conclusion You can choose either approach depending on your use case. The first approach works well for dynamic, real-time missing translation checks as the user navigates the app. The second is more suitable if you want a pre-defined overview of all missing keys without having to traverse through the app.
Let me know which approach works best for your use case!
The idea I have in mind would be: Expect the English translation is complete and compare it with the other ones, including non used strings.
ok. then chatGPT proposes a solution. will test that:
To implement this translation comparison logic within a React page, you can use a similar approach as the script I described but move the logic into a React component. This component can display the missing and extra keys in the browser.
Here’s a step-by-step guide on how to create a React page that compares the translation JSON files and shows missing and extra keys dynamically:
Step 1: Organize Your Translation Files Assume your translation files are stored locally in the public folder, like this:
public/ ├── translations/ │ ├── en.json │ ├── fr.json │ ├── de.json │ ├── es.json
These will be fetched dynamically within the React app.
Step 2: Create the React Component Here’s a React component that fetches the translation files, compares them, and displays missing and extra keys for each language.
import React, { useEffect, useState } from 'react';
// Recursive function to find missing keys
const findMissingKeys = (baseObj, otherObj, parentKey = '') => {
const missingKeys = [];
Object.keys(baseObj).forEach((key) => {
const fullKey = parentKey ? `${parentKey}.${key}` : key;
if (typeof baseObj[key] === 'object' && !Array.isArray(baseObj[key])) {
missingKeys.push(...findMissingKeys(baseObj[key], otherObj[key] || {}, fullKey));
} else if (!(key in otherObj)) {
missingKeys.push(fullKey);
}
});
return missingKeys;
};
// Recursive function to find extra keys
const findExtraKeys = (baseObj, otherObj, parentKey = '') => {
const extraKeys = [];
Object.keys(otherObj).forEach((key) => {
const fullKey = parentKey ? `${parentKey}.${key}` : key;
if (typeof otherObj[key] === 'object' && !Array.isArray(otherObj[key])) {
extraKeys.push(...findExtraKeys(baseObj[key] || {}, otherObj[key], fullKey));
} else if (!(key in baseObj)) {
extraKeys.push(fullKey);
}
});
return extraKeys;
};
const TranslationComparison = () => {
const [translations, setTranslations] = useState({});
const [missingKeys, setMissingKeys] = useState({});
const [extraKeys, setExtraKeys] = useState({});
const [loading, setLoading] = useState(true);
const languages = ['en', 'fr', 'de', 'es']; // Add other languages as needed
const baseLang = 'en'; // Base language to compare against
useEffect(() => {
// Fetch all translation files
const fetchTranslations = async () => {
const fetchedTranslations = {};
for (let lang of languages) {
const response = await fetch(`/translations/${lang}.json`);
const data = await response.json();
fetchedTranslations[lang] = data;
}
setTranslations(fetchedTranslations);
setLoading(false);
};
fetchTranslations();
}, []);
useEffect(() => {
if (!loading && translations[baseLang]) {
const baseTranslations = translations[baseLang];
const missing = {};
const extra = {};
// Compare each language against the base language
languages.forEach((lang) => {
if (lang !== baseLang) {
const otherTranslations = translations[lang];
missing[lang] = findMissingKeys(baseTranslations, otherTranslations);
extra[lang] = findExtraKeys(baseTranslations, otherTranslations);
}
});
setMissingKeys(missing);
setExtraKeys(extra);
}
}, [loading, translations]);
if (loading) return <p>Loading translations...</p>;
return (
<div>
<h1>Translation Comparison</h1>
{languages.map((lang) => (
lang !== baseLang && (
<div key={lang}>
<h2>Language: {lang.toUpperCase()}</h2>
<h3>Missing Keys:</h3>
{missingKeys[lang]?.length > 0 ? (
<ul>
{missingKeys[lang].map((key, index) => (
<li key={index}>{key}</li>
))}
</ul>
) : (
<p>No missing keys.</p>
)}
<h3>Extra Keys:</h3>
{extraKeys[lang]?.length > 0 ? (
<ul>
{extraKeys[lang].map((key, index) => (
<li key={index}>{key}</li>
))}
</ul>
) : (
<p>No extra keys.</p>
)}
</div>
)
))}
</div>
);
};
export default TranslationComparison;
Step 3: Explanation Fetching the JSON files:
In the useEffect hook, the component fetches the translation files for each language from the /locales folder using fetch. These are stored in the translations state object, where each language's translations are keyed by the language code (e.g., en, fr, de). Comparing translations:
Once all the translation files are fetched, the code compares the translations using the recursive findMissingKeys and findExtraKeys functions. It compares each language’s translation against the base language (English, in this case) and stores the missing and extra keys in the missingKeys and extraKeys state objects. Displaying results:
For each language other than the base language, the component displays the list of missing and extra keys. If no missing or extra keys are found, a message like "No missing keys" or "No extra keys" is shown. Step 4: Styling and Routing (Optional) If you want to add this page to your app's navigation, you can include it in your App.js or routes configuration like this:
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import TranslationComparison from './TranslationComparison';
function App() {
return (
<Router>
<Routes>
<Route path="/translation-comparison" element={<TranslationComparison />} />
{/* Other routes */}
</Routes>
</Router>
);
}
export default App;
Step 5: Testing Ensure your translation files (en.json, fr.json, de.json, etc.) are located in the public/translations/ folder. Visit the /translation-comparison page in your app to see the missing and extra keys for each language compared to the base language. Conclusion This React page dynamically loads and compares the translation files, showing any missing or extra keys for each language. You can expand it to support more languages, improve the styling, or add features like exporting the results to a file.
Let me know if you need any additional details!
It may be helpful to have a page that shows missing translation-strings, like the one with the missing tonie information.