Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add i18n checks to PR workflows #8411

Merged
merged 3 commits into from
Oct 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 46 additions & 13 deletions .github/workflows/build_and_test_workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ env:
NODE_OPTIONS: '--max-old-space-size=6144 --dns-result-order=ipv4first'

jobs:
build-lint-test:
build-test:
name: Build and Verify on ${{ matrix.name }} (ciGroup${{ matrix.group }})
strategy:
fail-fast: false
Expand Down Expand Up @@ -104,18 +104,6 @@ jobs:
if: matrix.os == 'windows-latest'
run: yarn osd bootstrap || yarn osd bootstrap

- name: Run linter
# ciGroup 1 of unit-tests is shorter and Linux is faster
if: matrix.group == 1 && matrix.os == 'ubuntu-latest'
id: linter
run: yarn lint

- name: Validate NOTICE file
# ciGroup 1 of unit-tests is shorter and Linux is faster
if: matrix.group == 1 && matrix.os == 'ubuntu-latest'
id: notice-validate
run: yarn notice:validate

- name: Run unit tests group ${{ matrix.group }} with coverage
id: unit-tests
run: yarn test:jest:ci:coverage --ci-group=${{ matrix.group }}
Expand All @@ -140,6 +128,51 @@ jobs:
id: integration-tests
run: yarn test:jest_integration:ci

lint-and-validate:
name: Lint and validate
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Setup Node
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
registry-url: 'https://registry.npmjs.org'

- name: Setup Yarn
run: |
npm uninstall -g yarn
npm i -g [email protected]
yarn config set network-timeout 1000000 -g

- name: Configure Yarn Cache
run: echo "YARN_CACHE_LOCATION=$(yarn cache dir)" >> $GITHUB_ENV

- name: Initialize Yarn Cache
uses: actions/cache@v3
with:
path: ${{ env.YARN_CACHE_LOCATION }}
key: yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-

- name: Run bootstrap
run: yarn osd bootstrap

- name: Run linter
id: linter
run: yarn lint

- name: Validate NOTICE file
id: notice-validate
run: yarn notice:validate

- name: Check i18n
id: i18n-check
run: yarn i18n:check

functional-tests:
name: Run functional tests on ${{ matrix.name }} (ciGroup${{ matrix.group }})
strategy:
Expand Down
9 changes: 9 additions & 0 deletions changelogs/fragments/8411.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
infra:
- Add i18n checks to PR workflows ([#8411](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8411))

feat:
- Ignore missing `formats` while checking locale files ([#8411](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8411))
- Add help text and description to `i18n-check` ([#8411](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8411))

fix:
- Fix malformed translations ([#8411](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8411))
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
"lint": "yarn run lint:es && yarn run lint:style",
"lint:es": "scripts/use_node scripts/eslint",
"lint:style": "scripts/use_node scripts/stylelint",
"i18n:check": "scripts/use_node scripts/i18n_check --ignore-missing --ignore-unused",
"i18n:extract": "scripts/use_node scripts/i18n_extract.js",
"makelogs": "scripts/use_node scripts/makelogs",
"uiFramework:compileCss": "cd packages/osd-ui-framework && yarn compileCss",
"osd:watch": "scripts/use_node scripts/opensearch_dashboards --dev --logging.json=false",
Expand Down
7 changes: 6 additions & 1 deletion src/dev/i18n/integrate_locale_files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
ignoreIncompatible: boolean;
ignoreUnused: boolean;
ignoreMissing: boolean;
ignoreMissingFormats?: boolean;
config: I18nConfig;
log: ToolingLog;
}
Expand Down Expand Up @@ -211,7 +212,11 @@
) {
const localizedMessages = JSON.parse((await readFileAsync(options.sourceFileName)).toString());
if (!localizedMessages.formats) {
throw createFailError(`Locale file should contain formats object.`);
if (options.ignoreMissingFormats) {
options.log.warning('Missing formats object ignored');

Check warning on line 216 in src/dev/i18n/integrate_locale_files.ts

View check run for this annotation

Codecov / codecov/patch

src/dev/i18n/integrate_locale_files.ts#L216

Added line #L216 was not covered by tests
} else {
throw createFailError(`Locale file should contain formats object.`);

Check warning on line 218 in src/dev/i18n/integrate_locale_files.ts

View check run for this annotation

Codecov / codecov/patch

src/dev/i18n/integrate_locale_files.ts#L218

Added line #L218 was not covered by tests
}
}

const localizedMessagesMap: LocalizedMessageMap = new Map(
Expand Down
13 changes: 11 additions & 2 deletions src/dev/i18n/tasks/check_compatibility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export interface I18nFlags {
ignoreIncompatible: boolean;
ignoreUnused: boolean;
ignoreMissing: boolean;
ignoreMissingFormats: boolean;
}

export function checkCompatibility(
Expand All @@ -47,16 +48,24 @@ export function checkCompatibility(
if (!config) {
throw new Error('Config is missing');
}
const { fix, ignoreIncompatible, ignoreUnused, ignoreMalformed, ignoreMissing } = flags;
const {
fix,
ignoreIncompatible,
ignoreUnused,
ignoreMalformed,
ignoreMissing,
ignoreMissingFormats,
} = flags;
return config.translations.map((translationsPath) => ({
task: async ({ messages }: { messages: Map<string, { message: string }> }) => {
// If `fix` is set we should try apply all possible fixes and override translations file.
// If `fix` is set we should try to apply all possible fixes and override translations file.
await integrateLocaleFiles(messages, {
dryRun: !fix,
ignoreIncompatible: fix || ignoreIncompatible,
ignoreUnused: fix || ignoreUnused,
ignoreMissing: fix || ignoreMissing,
ignoreMalformed: fix || ignoreMalformed,
ignoreMissingFormats,
sourceFileName: translationsPath,
targetFileName: fix ? translationsPath : undefined,
config,
Expand Down
16 changes: 15 additions & 1 deletion src/dev/run_i18n_check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { DEFAULT_DIRS_WITH_RC_FILES } from './i18n/constants';

const skipOnNoTranslations = (context: ListrContext) =>
!context.config?.translations?.length && 'No translations found.';

run(
async ({
flags: {
Expand All @@ -54,6 +55,7 @@ run(
'ignore-unused': ignoreUnused,
'include-config': includeConfig,
'ignore-untracked': ignoreUntracked,
'ignore-missing-formats': ignoreMissingFormats,
fix = false,
path,
},
Expand Down Expand Up @@ -121,11 +123,13 @@ run(
ignoreIncompatible: !!ignoreIncompatible,
ignoreUnused: !!ignoreUnused,
ignoreMissing: !!ignoreMissing,
// By default ignore missing formats
ignoreMissingFormats: ignoreMissingFormats !== false,
fix,
},
log
),
{ exitOnError: true }
{ exitOnError: false }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems changed the existing behavior, is it by intention?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah. If true, it exits at the first error. When false, it logs them all and then finally throws.

While "checking" we would rather have all of the problems shown to us, as opposed to just the first problem.

);
},
},
Expand Down Expand Up @@ -154,6 +158,16 @@ run(
flags: {
allowUnexpected: true,
guessTypesForUnexpectedFlags: true,
help: `
--ignore-incompatible Ignore mismatched keys in values and tokens in translations
--ignore-malformed Ignore malformed ICU format usages
--ignore-missing Ignore missing translations in locale files
--ignore-unused Ignore unused translations in locale files
--ignore-untracked Ignore untracked files with i18n labels
--ignore-missing-formats Ignore missing 'formats' key in locale files
(default: true, use --ignore-missing-formats=false to disable)
`,
},
description: 'Checks i18n usage in code and validates translation files',
}
);
4 changes: 2 additions & 2 deletions src/translations/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
"data.filter.filterBar.indexPatternSelectPlaceholder": "Ein Indexmuster auswählen",
"data.filter.filterBar.labelErrorInfo": "Indexmuster {indexPattern} nicht gefunden",
"data.filter.filterBar.labelErrorText": "Fehler",
"data.filter.filterBar.labelWarningInfo": "Feld {FieldName} in der aktuellen Ansicht nicht vorhanden",
"data.filter.filterBar.labelWarningInfo": "Feld {fieldName} in der aktuellen Ansicht nicht vorhanden",
"data.filter.filterBar.labelWarningText": "WARNUNG",
"data.filter.filterBar.moreFilterActionsMessage": "Filter: {innerText}. Diese Option für weitere Filteraktionen wählen.",
"data.filter.filterBar.negatedFilterPrefix": "NICHT ",
Expand Down Expand Up @@ -186,7 +186,7 @@
"dashboard.listing.createButtonText": "Erstellen Sie",
"dashboard.listing.createNewDashboard.combineDataViewFromOpenSearchDashboardsAppDescription": "Sie können Datenansichten aus jeder OpenSearch-Dashboards-App in einem Dashboard kombinieren und alles an einem Ort sehen.",
"dashboard.listing.createNewDashboard.createButtonLabel": "Neues Dashboard erstellen",
"dashboard.listing.createNewDashboard.newToOpenSearchDashboardsDescription": "Neu bei OpenSearch Dashboards? Öffnen Sie {SampleDataInstallLink} für einen Test.",
"dashboard.listing.createNewDashboard.newToOpenSearchDashboardsDescription": "Neu bei OpenSearch Dashboards? Öffnen Sie {sampleDataInstallLink} für einen Test.",
"dashboard.listing.createNewDashboard.sampleDataInstallLinkText": "Installieren Sie einige Beispieldaten",
"dashboard.listing.createNewDashboard.title": "Erstellen Sie Ihr erstes Dashboard",
"dashboard.listing.dashboardsTitle": "Dashboards",
Expand Down
2 changes: 1 addition & 1 deletion src/translations/es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
"dashboard.listing.createButtonText": "Cree",
"dashboard.listing.createNewDashboard.combineDataViewFromOpenSearchDashboardsAppDescription": "Puede combinar las vistas de datos de cualquier aplicación de OpenSearch Dashboards en un solo panel y ver todo en un mismo lugar.",
"dashboard.listing.createNewDashboard.createButtonLabel": "Crear un panel",
"dashboard.listing.createNewDashboard.newToOpenSearchDashboardsDescription": "¿Es la primera vez que usa OpenSearch Dashboards? {SampleDataInstallLink} para probarlo.",
"dashboard.listing.createNewDashboard.newToOpenSearchDashboardsDescription": "¿Es la primera vez que usa OpenSearch Dashboards? {sampleDataInstallLink} para probarlo.",
"dashboard.listing.createNewDashboard.sampleDataInstallLinkText": "Instale algunos datos de muestra",
"dashboard.listing.createNewDashboard.title": "Cree su primer panel",
"dashboard.listing.dashboardsTitle": "Paneles",
Expand Down
12 changes: 6 additions & 6 deletions src/translations/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
"data.filter.filterBar.filterItemBadgeIconAriaLabel": "Supprimer",
"data.filter.filterBar.includeFilterButtonLabel": "Inclure les résultats",
"data.filter.filterBar.indexPatternSelectPlaceholder": "Sélectionner un modèle d’index",
"data.filter.filterBar.labelErrorInfo": "Modèle d’index {IndexPattern} introuvable",
"data.filter.filterBar.labelErrorInfo": "Modèle d’index {indexPattern} introuvable",
"data.filter.filterBar.labelErrorText": "Erreur",
"data.filter.filterBar.labelWarningInfo": "Le champ {fieldName} n’existe pas dans la vue actuelle",
"data.filter.filterBar.labelWarningText": "Avertissement",
Expand Down Expand Up @@ -147,9 +147,9 @@
"dashboard.addExistingVisualizationLinkText": "Ajouter un existant",
"dashboard.addNewVisualizationText": "ou nouvel objet sur ce tableau de bord",
"dashboard.addPanel.noMatchingObjectsMessage": "Aucun objet correspondant n’a été trouvé.",
"dashboard.addPanel.savedObjectAddedToContainerSuccessMessageTitle": "{SavedObjectName} a été ajouté",
"dashboard.addPanel.savedObjectAddedToContainerSuccessMessageTitle": "{savedObjectName} a été ajouté",
"dashboard.addVisualizationLinkAriaLabel": "Ajouter une visualisation existante",
"dashboard.attributeService.saveToLibraryError": "Une erreur s’est produite lors de l’enregistrement. Erreur : {ErrorMessage}",
"dashboard.attributeService.saveToLibraryError": "Une erreur s’est produite lors de l’enregistrement. Erreur : {errorMessage}",
"dashboard.changeViewModeConfirmModal.cancelButtonLabel": "Poursuivre l’édition",
"dashboard.changeViewModeConfirmModal.confirmButtonLabel": "Annuler les modifications",
"dashboard.changeViewModeConfirmModal.discardChangesDescription": "Une fois que vous avez ignoré vos modifications, il n’est plus possible de les récupérer.",
Expand All @@ -163,7 +163,7 @@
"dashboard.dashboardGrid.toast.unableToLoadDashboardDangerMessage": "Impossible de charger le tableau de bord.",
"dashboard.dashboardListingDeleteErrorTitle": "Erreur de suppression du tableau de bord",
"dashboard.dashboardPageTitle": "Tableaux de bord",
"dashboard.dashboardWasNotSavedDangerMessage": "Le tableau de bord « {dashTitle} » n’a pas été enregistré. Erreur : {ErrorMessage}",
"dashboard.dashboardWasNotSavedDangerMessage": "Le tableau de bord « {dashTitle} » n’a pas été enregistré. Erreur : {errorMessage}",
"dashboard.dashboardWasSavedSuccessMessage": "Le tableau de bord « {dashTitle} » a été enregistré",
"dashboard.embedUrlParamExtension.filterBar": "Barre de filtrage",
"dashboard.embedUrlParamExtension.include": "Inclure",
Expand All @@ -186,7 +186,7 @@
"dashboard.listing.createButtonText": "Créez",
"dashboard.listing.createNewDashboard.combineDataViewFromOpenSearchDashboardsAppDescription": "Vous pouvez combiner les vues de données de n’importe quelle application OpenSearch Dashboards dans un seul tableau de bord et tout voir au même endroit.",
"dashboard.listing.createNewDashboard.createButtonLabel": "Créer un nouveau tableau de bord",
"dashboard.listing.createNewDashboard.newToOpenSearchDashboardsDescription": "Vous êtes nouveau sur OpenSearch Dashboards ? {SampleDataInstallLink} pour faire un essai routier.",
"dashboard.listing.createNewDashboard.newToOpenSearchDashboardsDescription": "Vous êtes nouveau sur OpenSearch Dashboards ? {sampleDataInstallLink} pour faire un essai routier.",
"dashboard.listing.createNewDashboard.sampleDataInstallLinkText": "Installer quelques exemples de données",
"dashboard.listing.createNewDashboard.title": "Créez votre premier tableau de bord",
"dashboard.listing.dashboardsTitle": "Tableaux de bord",
Expand Down Expand Up @@ -228,7 +228,7 @@
"dashboard.topNav.cloneModal.cloneDashboardModalHeaderTitle": "Tableau de bord cloné",
"dashboard.topNav.cloneModal.confirmButtonLabel": "Confirmer le clone",
"dashboard.topNav.cloneModal.confirmCloneDescription": "Confirmer le clone",
"dashboard.topNav.cloneModal.dashboardExistsDescription": "Cliquer sur {ConfirmClone} pour cloner le tableau de bord avec le titre dupliqué.",
"dashboard.topNav.cloneModal.dashboardExistsDescription": "Cliquer sur {confirmClone} pour cloner le tableau de bord avec le titre dupliqué.",
"dashboard.topNav.cloneModal.dashboardExistsTitle": "Un tableau de bord intitulé {newDashboardName} existe déjà.",
"dashboard.topNav.cloneModal.enterNewNameForDashboardDescription": "Entrez un nouveau nom pour votre tableau de bord.",
"dashboard.topNav.editSwitchLabel": "Modifier",
Expand Down
4 changes: 2 additions & 2 deletions src/translations/ko-KR.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@
"data.filter.filterBar.editFilterButtonLabel": "필터 편집",
"data.filter.filterBar.enableFilterButtonLabel": "다시 활성화",
"data.filter.filterBar.excludeFilterButtonLabel": "결과 제외",
"data.filter.filterBar.fieldNotFound": "{IndexPattern} 인덱스 패턴에서 {key} 필드를 찾을 수 없음",
"data.filter.filterBar.fieldNotFound": "{indexPattern} 인덱스 패턴에서 {key} 필드를 찾을 수 없음",
"data.filter.filterBar.filterItemBadgeAriaLabel": "필터 작업",
"data.filter.filterBar.filterItemBadgeIconAriaLabel": "삭제",
"data.filter.filterBar.includeFilterButtonLabel": "결과 포함",
"data.filter.filterBar.indexPatternSelectPlaceholder": "인덱스 패턴 선택",
"data.filter.filterBar.labelErrorInfo": "{IndexPattern} 인덱스 패턴을 찾을 수 없음",
"data.filter.filterBar.labelErrorInfo": "{indexPattern} 인덱스 패턴을 찾을 수 없음",
"data.filter.filterBar.labelErrorText": "오류",
"data.filter.filterBar.labelWarningInfo": "{fieldName} 필드가 현재 보기에 없음",
"data.filter.filterBar.labelWarningText": "경고",
Expand Down
4 changes: 2 additions & 2 deletions src/translations/tr-TR.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
"dashboard.listing.createButtonText": "Oluştur",
"dashboard.listing.createNewDashboard.combineDataViewFromOpenSearchDashboardsAppDescription": "Herhangi bir OpenSearch Dashboards uygulamasındaki veri görünümlerini tek bir panoda birleştirebilir ve her şeyi tek bir yerde görebilirsiniz.",
"dashboard.listing.createNewDashboard.createButtonLabel": "Yeni pano oluştur",
"dashboard.listing.createNewDashboard.newToOpenSearchDashboardsDescription": "OpenSearch Dashboards'da yeni misiniz? Deneme yapmak için {SampleDataInstallLink} bağlantısına tıklayın.",
"dashboard.listing.createNewDashboard.newToOpenSearchDashboardsDescription": "OpenSearch Dashboards'da yeni misiniz? Deneme yapmak için {sampleDataInstallLink} bağlantısına tıklayın.",
"dashboard.listing.createNewDashboard.sampleDataInstallLinkText": "Bazı örnek verileri yükleyin",
"dashboard.listing.createNewDashboard.title": "İlk panonuzu oluşturun",
"dashboard.listing.dashboardsTitle": "Panolar",
Expand Down Expand Up @@ -228,7 +228,7 @@
"dashboard.topNav.cloneModal.cloneDashboardModalHeaderTitle": "Panoyu kopyala",
"dashboard.topNav.cloneModal.confirmButtonLabel": "Kopyalamayı onayla",
"dashboard.topNav.cloneModal.confirmCloneDescription": "Kopyalamayı onayla",
"dashboard.topNav.cloneModal.dashboardExistsDescription": "Yinelenen başlıklı panoyu kopyalamak için {ConfirmClone} öğesine tıklayın.",
"dashboard.topNav.cloneModal.dashboardExistsDescription": "Yinelenen başlıklı panoyu kopyalamak için {confirmClone} öğesine tıklayın.",
"dashboard.topNav.cloneModal.dashboardExistsTitle": "{newDashboardName} başlıklı bir pano zaten var.",
"dashboard.topNav.cloneModal.enterNewNameForDashboardDescription": "Lütfen panonuz için yeni bir ad girin.",
"dashboard.topNav.editSwitchLabel": "Düzenle",
Expand Down
2 changes: 1 addition & 1 deletion src/translations/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@
"dashboard.dashboardGrid.toast.unableToLoadDashboardDangerMessage": "无法加载控制面板。",
"dashboard.dashboardListingDeleteErrorTitle": "删除控制面板时出错",
"dashboard.dashboardPageTitle": "控制面板",
"dashboard.dashboardWasNotSavedDangerMessage": "控制面板“{dashTitle}”未保存。错误: ",
"dashboard.dashboardWasNotSavedDangerMessage": "控制面板“{dashTitle}”未保存。错误: {errorMessage}",
"dashboard.dashboardWasSavedSuccessMessage": "控制面板“{dashTitle}”已保存",
"dashboard.embedUrlParamExtension.filterBar": "筛选栏",
"dashboard.embedUrlParamExtension.include": "包括",
Expand Down
Loading