mirror of
https://github.com/suitenumerique/docs.git
synced 2026-10-16 21:25:39 +02:00
🌐(i18n) key crowdin frontend exports by locale
Crowdin exported the frontend translations to a folder named after %two_letters_code%, so every regional variant of a language landed in the same folder: zh-CN and zh-TW both gave "zh", and the last one downloaded overwrote the other. Mapping the Chinese locales apart would have fixed only that pair, and each new variant (pt-BR, es-MX, en-GB...) would have needed its own entry. Use %locale% instead, which is unique per locale, like the backend already does with %locale_with_underscore%. format-deploy keeps the keys of translations.json as they are today (fr, de...) and only keeps the region (zh-cn, zh-tw) when several locales of the same language are translated. The language detection and the language picker already match a locale exactly before falling back to its language, so they need no change. The export pattern is stored by Crowdin with the source file: the sources must be uploaded again before the next download, and the zh-CN data that was polluted with Traditional Chinese still needs to be re-uploaded. Fixes #2749
This commit is contained in:
@@ -100,6 +100,7 @@ and this project adheres to
|
||||
- 🐛(frontend) fix redirect after deleting a document #2706
|
||||
- 🐛(frontend) keep documents draggable with a mouse when zoomed #2727
|
||||
- 📝(docs) fix markdown typo in installation README #2766
|
||||
- 🐛(i18n) export locale correctly #2750
|
||||
|
||||
### Removed
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ files: [
|
||||
{
|
||||
source: "/frontend/packages/i18n/locales/impress/translations-crowdin.json",
|
||||
dest: "/frontend-impress.json",
|
||||
translation: "/frontend/packages/i18n/locales/impress/%two_letters_code%/translations.json",
|
||||
translation: "/frontend/packages/i18n/locales/impress/%locale%/translations.json",
|
||||
skip_untranslated_strings: true,
|
||||
},
|
||||
]
|
||||
|
||||
@@ -60,6 +60,36 @@ describe('integration testing on i18n package', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('cmd format-deploy keeps the region only when locales collide', () => {
|
||||
// To be sure the tests folder is not here
|
||||
fs.rmSync('./locales/tests', { recursive: true, force: true });
|
||||
|
||||
const locales = {
|
||||
en: 'My test',
|
||||
'fr-FR': 'Mon test',
|
||||
'zh-CN': '简',
|
||||
'zh-TW': '繁',
|
||||
};
|
||||
Object.entries(locales).forEach(([locale, message]) => {
|
||||
fs.mkdirSync(`./locales/tests/${locale}/`, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
`./locales/tests/${locale}/translations.json`,
|
||||
JSON.stringify({ test: { message } }),
|
||||
'utf8',
|
||||
);
|
||||
});
|
||||
|
||||
const output = './locales/tests/translations.json';
|
||||
execSync(`node ./format-deploy.mjs --app=tests --output=${output}`);
|
||||
const json = JSON.parse(fs.readFileSync(output, 'utf8'));
|
||||
expect(json).toEqual({
|
||||
en: { translation: { test: 'My test' } },
|
||||
fr: { translation: { test: 'Mon test' } },
|
||||
'zh-cn': { translation: { test: '简' } },
|
||||
'zh-tw': { translation: { test: '繁' } },
|
||||
});
|
||||
});
|
||||
|
||||
test('cmd format-deploy throws an error when translation file is not found', () => {
|
||||
// To be sure the tests folder is not here
|
||||
fs.rmSync('./locales/tests', { recursive: true, force: true });
|
||||
|
||||
@@ -11,13 +11,31 @@ const folderPath = './locales/' + app;
|
||||
const namefile = 'translations.json';
|
||||
const jsonI18n = {};
|
||||
|
||||
// Fetch the files in the locales folder
|
||||
fs.readdirSync(folderPath).map((language) => {
|
||||
const languagePath = path.join(folderPath, path.sep, language);
|
||||
// Crowdin outputs one folder per locale (fr-FR, zh-CN, zh-TW, en...)
|
||||
const locales = fs.readdirSync(folderPath).filter((entry) => {
|
||||
// Crowdin output file in folder, we want to treat only these ones
|
||||
if (!fs.lstatSync(languagePath).isDirectory()) {
|
||||
return;
|
||||
}
|
||||
return fs.lstatSync(path.join(folderPath, entry)).isDirectory();
|
||||
});
|
||||
|
||||
const getLanguage = (locale) => locale.split(/[-_]/)[0].toLowerCase();
|
||||
|
||||
const localesPerLanguage = {};
|
||||
locales.forEach((locale) => {
|
||||
const language = getLanguage(locale);
|
||||
localesPerLanguage[language] = (localesPerLanguage[language] ?? 0) + 1;
|
||||
});
|
||||
|
||||
// The language alone (fr) is enough when only one locale of it is translated,
|
||||
// the region is kept (zh-cn, zh-tw) when the locales would otherwise collide.
|
||||
const getKey = (locale) =>
|
||||
localesPerLanguage[getLanguage(locale)] > 1
|
||||
? locale.replace('_', '-').toLowerCase()
|
||||
: getLanguage(locale);
|
||||
|
||||
// Fetch the files in the locales folder
|
||||
locales.forEach((locale) => {
|
||||
const language = getKey(locale);
|
||||
const languagePath = path.join(folderPath, path.sep, locale);
|
||||
|
||||
jsonI18n[language] = {
|
||||
translation: {},
|
||||
|
||||
Reference in New Issue
Block a user