Skip to content

Commit

Permalink
Add i18n checks to PR workflows (#8411)
Browse files Browse the repository at this point in the history
* Ignore missing `formats` while checking locale files

Also:
* Add help text and description to `i18n-check`
* Fix malformed translations

Signed-off-by: Miki <[email protected]>

* Add i18n checks to PR workflows

Signed-off-by: Miki <[email protected]>

* Changeset file for PR #8411 created/updated

---------

Signed-off-by: Miki <[email protected]>
Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com>
  • Loading branch information
1 parent e077644 commit 428a7c2
Show file tree
Hide file tree
Showing 12 changed files with 103 additions and 31 deletions.
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 @@ export interface IntegrateOptions {
ignoreIncompatible: boolean;
ignoreUnused: boolean;
ignoreMissing: boolean;
ignoreMissingFormats?: boolean;
config: I18nConfig;
log: ToolingLog;
}
Expand Down Expand Up @@ -211,7 +212,11 @@ export async function integrateLocaleFiles(
) {
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');
} else {
throw createFailError(`Locale file should contain formats object.`);
}
}

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 }
);
},
},
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

0 comments on commit 428a7c2

Please sign in to comment.