fix: flatten i18n keys (#2483)

This commit is contained in:
Whitewater
2023-05-22 01:08:43 -07:00
committed by GitHub
parent 8aab1d6459
commit fe5be0cb47
2 changed files with 58 additions and 8 deletions

View File

@@ -5,11 +5,15 @@ import path from 'node:path';
import { format } from 'prettier';
import { getAllProjectLanguages, getRemoteTranslations } from './api.js';
import type { TranslationRes } from './utils.js';
import { flattenTranslation, type TranslationRes } from './utils.js';
const INDENT = 2;
const RES_DIR = path.resolve(process.cwd(), 'src', 'resources');
const countKeys = (obj: TranslationRes) => {
const countKeys = (obj: TranslationRes | null) => {
if (!obj) {
return 0;
}
let count = 0;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
Object.entries(obj).forEach(([_, value]) => {
@@ -42,6 +46,12 @@ const getBaseTranslations = async (baseLanguage: { tag: string }) => {
};
const main = async () => {
try {
fs.access(RES_DIR);
} catch (error) {
fs.mkdir(RES_DIR);
console.log('Create directory', RES_DIR);
}
console.log('Loading project languages...');
const languages = await getAllProjectLanguages();
const baseLanguage = languages.find(language => language.base);
@@ -58,11 +68,17 @@ const main = async () => {
console.log(`Loading ${language.tag} translations...`);
const translations = await getRemoteTranslations(language.tag);
const keyNum = countKeys(translations);
const completeRate = Number((keyNum / baseKeyNum).toFixed(3));
console.log(
`Load ${language.name} ${
completeRate * 100
}, %(${keyNum}/${baseKeyNum}) complete`
);
return {
...language,
translations,
completeRate: keyNum / baseKeyNum,
completeRate,
};
})
);
@@ -81,10 +97,10 @@ const main = async () => {
{
'// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.':
'',
...language.translations,
...flattenTranslation(language.translations),
},
null,
4
INDENT
) + '\n'
);
});
@@ -92,7 +108,7 @@ const main = async () => {
console.log('Generating meta data...');
const code = `// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
// Run \`yarn run download-resources\` to regenerate.
// To overwrite this, please overwrite download.ts script.
// If you need to update the code, please edit \`i18n/src/scripts/download.ts\` inside your project.
${availableLanguages
.map(
language =>
@@ -100,14 +116,16 @@ const main = async () => {
language.tag
}.json'`
)
.sort()
.join('\n')}
export const LOCALES = [
${availableLanguages
// eslint-disable-next-line @typescript-eslint/no-unused-vars
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- omit key
.map(({ translations, ...language }) =>
JSON.stringify({
...language,
// a trick to generate a string without quotation marks
res: '__RES_PLACEHOLDER',
}).replace(
'"__RES_PLACEHOLDER"',
@@ -124,7 +142,7 @@ const main = async () => {
parser: 'typescript',
singleQuote: true,
trailingComma: 'es5',
tabWidth: 4,
tabWidth: INDENT,
arrowParens: 'avoid',
})
);

View File

@@ -1,3 +1,35 @@
export interface TranslationRes {
[x: string]: string | TranslationRes;
}
/**
* Recursively flattens a JSON object using dot notation.
*
* NOTE: input must be an object as described by JSON spec. Arbitrary
* JS objects (e.g. {a: () => 42}) may result in unexpected output.
* MOREOVER, it removes keys with empty objects/arrays as value (see
* examples bellow).
*
* @example
* flattenTranslation({a: 1, b: [{c: 2, d: {e: 3}}, 4]})
* // {a: 1, b.0.c: 2, b.0.d.e: 3, b.1: 4}
* flattenTranslation({a: 1, b: [{c: 2, d: {e: [true, false, {f: 1}]}}]})
* // {a: 1, b.0.c: 2, b.0.d.e.0: true, b.0.d.e.1: false, b.0.d.e.2.f: 1}
* flattenTranslation({a: 1, b: [], c: {}})
* // {a: 1}
*
* @param obj item to be flattened
*/
export const flattenTranslation = (
obj: string | TranslationRes,
path?: string
): TranslationRes => {
if (!(obj instanceof Object)) return { [path ?? '']: obj };
return Object.keys(obj).reduce((output, key) => {
return {
...output,
...flattenTranslation(obj[key], path ? path + '.' + key : key),
};
}, {});
};