Fix missing type support for i18n resources

Due to the change in 65a9a905be - moving to a json file - it wasn't ensured anymore that IsoLanguages contained the i18n language codes
This commit is contained in:
schroda
2026-01-30 21:46:20 +01:00
parent c5cdd347b7
commit edbbc81575
5 changed files with 47 additions and 37 deletions

View File

@@ -13,7 +13,7 @@ import { formatter } from '@lingui/format-po';
// eslint-disable-next-line import/no-default-export // eslint-disable-next-line import/no-default-export
export default defineConfig({ export default defineConfig({
sourceLocale: 'en', sourceLocale: 'en',
// This array is auto-generated by tools/scripts/weblate/generatei18nResources.ts. Do not edit manually. // !!!IMPORTANT!!!: 'locales' is auto-generated by tools/scripts/weblate/generatei18nResources.ts. Do not edit manually.
locales: [ locales: [
'de', 'de',
'en', 'en',

View File

@@ -34,7 +34,7 @@ import { LibraryOptions } from '@/features/library/Library.types.ts';
import { MetadataThemeSettings } from '@/features/theme/AppTheme.types.ts'; import { MetadataThemeSettings } from '@/features/theme/AppTheme.types.ts';
import { TapZoneInvertMode } from '@/features/reader/tap-zones/TapZoneLayout.types.ts'; import { TapZoneInvertMode } from '@/features/reader/tap-zones/TapZoneLayout.types.ts';
import { detectLocale, getISOLanguage } from '@/lib/ISOLanguageUtil.ts'; import { detectLocale, getISOLanguage } from '@/lib/ISOLanguageUtil.ts';
import { i18nResources } from '@/i18n'; import { I18nResourceCode, i18nResources } from '@/i18n';
import { toUniqueLanguageCodes } from '@/base/utils/Languages.ts'; import { toUniqueLanguageCodes } from '@/base/utils/Languages.ts';
export const APP_METADATA_KEY_PREFIX = 'webUI'; export const APP_METADATA_KEY_PREFIX = 'webUI';
@@ -385,7 +385,7 @@ export const APP_METADATA: Record<
toConstrainedValue: (value: string) => { toConstrainedValue: (value: string) => {
const locale = getISOLanguage(value)?.isoCode; const locale = getISOLanguage(value)?.isoCode;
if (!locale || !i18nResources.includes(locale)) { if (!locale || !i18nResources.includes(locale as I18nResourceCode)) {
return detectLocale(); return detectLocale();
} }

View File

@@ -10,7 +10,14 @@ import { i18n } from '@lingui/core';
import { detectLocale, SELECTED_LANGUAGE_CODE_KEY } from '@/lib/ISOLanguageUtil.ts'; import { detectLocale, SELECTED_LANGUAGE_CODE_KEY } from '@/lib/ISOLanguageUtil.ts';
import { AppStorage } from '@/lib/storage/AppStorage.ts'; import { AppStorage } from '@/lib/storage/AppStorage.ts';
// This array is auto-generated by tools/scripts/weblate/generatei18nResources.ts. Do not edit manually. /**
* Keys have to match {@link IsoLanguages} codes, they're used for showing the language name in the dropdown in the {@link Settings}.<br/>
* In case there is no language code for the key in {@link IsoLanguages}, the corresponding language has to be added
*
* **IMPORTANT:**
*
* This is generated by the `i18n:gen-resources` script
*/
export const i18nResources = [ export const i18nResources = [
'de', 'de',
'en', 'en',

View File

@@ -1,23 +0,0 @@
{
"comment": "!!!IMPORTANT!!!: 'locales' is auto generated by tools/scripts/weblate/generatei18nResources.ts. Do not edit manually.",
"locales": [
"de",
"en",
"es",
"fa",
"fr",
"hu",
"it",
"ja",
"ko",
"pl",
"pt",
"ru",
"ta",
"tr",
"vi",
"zh_Hans",
"zh_Hant"
],
"defaultLocale": "en"
}

View File

@@ -17,9 +17,11 @@ import {
import { WeblateLanguageStatistic } from './Weblate.types.ts'; import { WeblateLanguageStatistic } from './Weblate.types.ts';
import { TRANSLATED_PERCENT_THRESHOLD } from './Weblate.constants.ts'; import { TRANSLATED_PERCENT_THRESHOLD } from './Weblate.constants.ts';
const FILE_PATH = 'src/i18n/locales.json'; const I18N_INDEX_PATH = 'src/i18n/index.ts';
const LINGUI_CONFIG_PATH = 'lingui.config.ts';
const outputFilePath = path.join(import.meta.dirname, `../../../${FILE_PATH}`); const i18nIndexFilePath = path.join(import.meta.dirname, `../../../${I18N_INDEX_PATH}`);
const linguiConfigFilePath = path.join(import.meta.dirname, `../../../${LINGUI_CONFIG_PATH}`);
const resourcesDirPath = path.join(import.meta.dirname, '../../../src/i18n/locales'); const resourcesDirPath = path.join(import.meta.dirname, '../../../src/i18n/locales');
const extractLanguageCode = (resourceFileNames: string): string => const extractLanguageCode = (resourceFileNames: string): string =>
@@ -43,6 +45,25 @@ const meetsTranslatedPercentThreshold = (
return meetsThreshold; return meetsThreshold;
}; };
const formatLocalesArray = (locales: string[]): string => locales.map((locale) => `'${locale}'`).join(', ');
const updateI18nIndex = (locales: string[]): void => {
const content = fs.readFileSync(i18nIndexFilePath, 'utf-8');
const localesArrayStr = formatLocalesArray(locales);
const updatedContent = content.replace(
/(export const i18nResources = \[)[\s\S]*?(] as const)/g,
`$1${localesArrayStr}$2`,
);
fs.writeFileSync(i18nIndexFilePath, updatedContent);
};
const updateLinguiConfig = (locales: string[]): void => {
const content = fs.readFileSync(linguiConfigFilePath, 'utf-8');
const localesArrayStr = formatLocalesArray(locales);
const updatedContent = content.replace(/(locales: \[)[\s\S]*?(],)/g, `$1${localesArrayStr}$2`);
fs.writeFileSync(linguiConfigFilePath, updatedContent);
};
const generateResources = async () => { const generateResources = async () => {
const weblateLanguageStats = await fetchWeblateLanguageStats(); const weblateLanguageStats = await fetchWeblateLanguageStats();
@@ -52,21 +73,26 @@ const generateResources = async () => {
.filter((resourceFileName) => meetsTranslatedPercentThreshold(resourceFileName, weblateLanguageStats)) .filter((resourceFileName) => meetsTranslatedPercentThreshold(resourceFileName, weblateLanguageStats))
.map(extractLanguageCode); .map(extractLanguageCode);
const localesConfig = JSON.parse(fs.readFileSync(outputFilePath, 'utf-8')); updateI18nIndex(resourceNames);
updateLinguiConfig(resourceNames);
localesConfig.locales = resourceNames;
fs.writeFileSync(outputFilePath, `${JSON.stringify(localesConfig, null, 2)}\n`);
execSync('yarn tsc', { stdio: 'inherit' }); execSync('yarn tsc', { stdio: 'inherit' });
execSync(`yarn eslint --fix ${I18N_INDEX_PATH} ${LINGUI_CONFIG_PATH}`, { stdio: 'inherit' });
const hasFileChanged = execSync('git status --porcelain').toString().includes(FILE_PATH); const hasI18nIndexChanged = execSync('git status --porcelain').toString().includes(I18N_INDEX_PATH);
if (!hasFileChanged) { const hasLinguiConfigChanged = execSync('git status --porcelain').toString().includes(LINGUI_CONFIG_PATH);
if (!hasI18nIndexChanged && !hasLinguiConfigChanged) {
return; return;
} }
execSync('git reset'); execSync('git reset');
execSync(`git add ${FILE_PATH}`); if (hasI18nIndexChanged) {
execSync(`git add ${I18N_INDEX_PATH}`);
}
if (hasLinguiConfigChanged) {
execSync(`git add ${LINGUI_CONFIG_PATH}`);
}
execSync('git commit -m "Update available languages"'); execSync('git commit -m "Update available languages"');
}; };