Automate L10n extraction locally and in CI (#12800)
* Extract locales automatically in CI * TMP: minor fixes
This commit is contained in:
Родитель
4c517c1b7c
Коммит
4e1d5f6691
|
@ -0,0 +1,114 @@
|
|||
name: 'Dump Context'
|
||||
description: 'Display context for action run'
|
||||
|
||||
outputs:
|
||||
# All github action outputs are strings, even if set to "true"
|
||||
# so when using these values always assert against strings or convert from json
|
||||
# \$\{{ needs.context.outputs.is_fork == 'true' }} // true
|
||||
# \$\{{ fromJson(needs.context.outputs.is_fork) == false }} // true
|
||||
# \$\{{ needs.context.outputs.is_fork == true }} // false
|
||||
# \$\{{ needs.context.outputs.is_fork }} // false
|
||||
is_fork:
|
||||
description: ""
|
||||
value: ${{ steps.context.outputs.is_fork }}
|
||||
is_default_branch:
|
||||
description: ""
|
||||
value: ${{ steps.context.outputs.is_default_branch }}
|
||||
is_release_master:
|
||||
description: ""
|
||||
value: ${{ steps.context.outputs.is_release_master }}
|
||||
is_release_tag:
|
||||
description: ""
|
||||
value: ${{ steps.context.outputs.is_release_tag }}
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- name: Dump job context
|
||||
shell: bash
|
||||
env:
|
||||
JOB_CONTEXT: ${{ toJson(job) }}
|
||||
run: echo "$JOB_CONTEXT"
|
||||
- name: Dump steps context
|
||||
shell: bash
|
||||
env:
|
||||
STEPS_CONTEXT: ${{ toJson(steps) }}
|
||||
run: echo "$STEPS_CONTEXT"
|
||||
- name: Dump runner context
|
||||
shell: bash
|
||||
env:
|
||||
RUNNER_CONTEXT: ${{ toJson(runner) }}
|
||||
run: echo "$RUNNER_CONTEXT"
|
||||
- name: Dump env context
|
||||
shell: bash
|
||||
env:
|
||||
ENV_CONTEXT: ${{ toJson(env) }}
|
||||
run: |
|
||||
echo "$ENV_CONTEXT"
|
||||
- name: Dump inputs context
|
||||
shell: bash
|
||||
env:
|
||||
INPUTS_CONTEXT: ${{ toJson(inputs) }}
|
||||
run: |
|
||||
echo "$INPUTS_CONTEXT"
|
||||
|
||||
- name: Set context
|
||||
id: context
|
||||
env:
|
||||
# The default branch of the repository, in this case "master"
|
||||
default_branch: ${{ github.event.repository.default_branch }}
|
||||
shell: bash
|
||||
run: |
|
||||
event_name="${{ github.event_name }}"
|
||||
event_action="${{ github.event.action }}"
|
||||
|
||||
# Stable check for if the workflow is running on the default branch
|
||||
# https://stackoverflow.com/questions/64781462/github-actions-default-branch-variable
|
||||
is_default_branch="${{ format('refs/heads/{0}', env.default_branch) == github.ref }}"
|
||||
|
||||
# In most events, the epository refers to the head which would be the fork
|
||||
is_fork="${{ github.event.repository.fork }}"
|
||||
|
||||
# This is different in a pull_request where we need to check the head explicitly
|
||||
if [[ "${{ github.event_name }}" == 'pull_request' ]]; then
|
||||
# repository on a pull request refers to the base which is always mozilla/addons-server
|
||||
is_head_fork="${{ github.event.pull_request.head.repo.fork }}"
|
||||
# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions
|
||||
is_dependabot="${{ github.actor == 'dependabot[bot]' }}"
|
||||
|
||||
# If the head repository is a fork or if the PR is opened by dependabot
|
||||
# we consider the run to be a fork. Dependabot and proper forks are treated
|
||||
# the same in terms of limited read only github token scope
|
||||
if [[ "$is_head_fork" == 'true' || "$is_dependabot" == 'true' ]]; then
|
||||
is_fork="true"
|
||||
fi
|
||||
fi
|
||||
|
||||
is_release_master="false"
|
||||
is_release_tag="false"
|
||||
|
||||
# Releases can only happen if we are NOT on a fork
|
||||
if [[ "$is_fork" == 'false' ]]; then
|
||||
# A master release occurs on a push to the default branch of the origin repository
|
||||
if [[ "$event_name" == 'push' && "$is_default_branch" == 'true' ]]; then
|
||||
is_release_master="true"
|
||||
fi
|
||||
|
||||
# A tag release occurs when a release is published
|
||||
if [[ "$event_name" == 'release' && "$event_action" == 'publish' ]]; then
|
||||
is_release_tag="true"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "is_default_branch=$is_default_branch" >> $GITHUB_OUTPUT
|
||||
echo "is_fork=$is_fork" >> $GITHUB_OUTPUT
|
||||
echo "is_release_master=$is_release_master" >> $GITHUB_OUTPUT
|
||||
echo "is_release_tag=$is_release_tag" >> $GITHUB_OUTPUT
|
||||
|
||||
echo "event_name: $event_name"
|
||||
cat $GITHUB_OUTPUT
|
|
@ -0,0 +1,68 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
context:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
is_fork: ${{ steps.context.outputs.is_fork }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- id: context
|
||||
uses: ./.github/actions/context
|
||||
|
||||
locales:
|
||||
needs: context
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install gettext
|
||||
run: sudo apt-get install gettext
|
||||
|
||||
- name: Yarn install
|
||||
run: yarn install --frozen-lockfile --prefer-offline
|
||||
|
||||
- name: Extract locales
|
||||
run: yarn extract-locales
|
||||
|
||||
- name: Push Locales
|
||||
run: |
|
||||
event_name="${{ github.event_name }}"
|
||||
is_fork="${{ needs.context.outputs.is_fork }}"
|
||||
|
||||
if [[ "$is_fork" == 'true' ]]; then
|
||||
cat <<'EOF'
|
||||
Github actions are not authorized to push from workflows triggered by forks.
|
||||
We cannot verify if the l10n extraction push will work or not.
|
||||
Please submit a PR from the base repository if you are modifying l10n extraction scripts.
|
||||
EOF
|
||||
exit 0
|
||||
fi
|
||||
|
||||
ARGS=""
|
||||
|
||||
if [[ "$event_name" == 'pull_request' ]]; then
|
||||
ARGS="--dry-run"
|
||||
fi
|
||||
|
||||
./bin/push-locales $ARGS
|
||||
|
||||
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
*.*
|
||||
# exclude these files
|
||||
Dockerfile
|
||||
src/fonts/LICENSE
|
||||
# exclude these directories
|
||||
/assets/
|
||||
/bin/
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
// Create UTC creation date in the correct format.
|
||||
const potCreationDate = new Date()
|
||||
.toISOString()
|
||||
.replace('T', ' ')
|
||||
.replace(/:\d{2}.\d{3}Z/, '+0000');
|
||||
|
||||
module.exports = {
|
||||
extends: './babel.config.js',
|
||||
plugins: [
|
||||
[
|
||||
'module:babel-gettext-extractor',
|
||||
{
|
||||
headers: {
|
||||
'Project-Id-Version': 'amo',
|
||||
'Report-Msgid-Bugs-To': 'EMAIL@ADDRESS',
|
||||
'POT-Creation-Date': potCreationDate,
|
||||
'PO-Revision-Date': 'YEAR-MO-DA HO:MI+ZONE',
|
||||
'Last-Translator': 'FULL NAME <EMAIL@ADDRESS>',
|
||||
'Language-Team': 'LANGUAGE <LL@li.org>',
|
||||
'MIME-Version': '1.0',
|
||||
'Content-Type': 'text/plain; charset=utf-8',
|
||||
'Content-Transfer-Encoding': '8bit',
|
||||
'plural-forms': 'nplurals=2; plural=(n!=1);',
|
||||
},
|
||||
functionNames: {
|
||||
gettext: ['msgid'],
|
||||
dgettext: ['domain', 'msgid'],
|
||||
ngettext: ['msgid', 'msgid_plural', 'count'],
|
||||
dngettext: ['domain', 'msgid', 'msgid_plural', 'count'],
|
||||
pgettext: ['msgctxt', 'msgid'],
|
||||
dpgettext: ['domain', 'msgctxt', 'msgid'],
|
||||
npgettext: ['msgctxt', 'msgid', 'msgid_plural', 'count'],
|
||||
dnpgettext: ['domain', 'msgctxt', 'msgid', 'msgid_plural', 'count'],
|
||||
},
|
||||
fileName: './locale/templates/LC_MESSAGES/amo.pot',
|
||||
baseDirectory: process.cwd(),
|
||||
stripTemplateLiteralIndent: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
|
@ -1,3 +1,59 @@
|
|||
#!/usr/bin/env sh
|
||||
#!/usr/bin/env zx
|
||||
|
||||
import {$, path, echo, within, glob} from 'zx';
|
||||
|
||||
const root = path.join(__dirname, '..');
|
||||
const localeDir = path.join(root, 'locale');
|
||||
const templateFile = path.join(localeDir, '/templates/LC_MESSAGES/amo.pot');
|
||||
|
||||
within(async () => {
|
||||
echo('Extracting locales...');
|
||||
|
||||
const sourceDir = path.join(root, 'src', 'amo');
|
||||
const outputDir = path.join(root, 'dist', 'locales');
|
||||
const localesConfig = path.join(root, 'babel.config.locales.js');
|
||||
|
||||
await $`babel ${sourceDir} \
|
||||
--out-dir ${outputDir} \
|
||||
--config-file ${localesConfig} \
|
||||
--verbose \
|
||||
`;
|
||||
|
||||
const {stdout: output} = await $`git diff --numstat -- ${templateFile}`;
|
||||
|
||||
// git diff --numstat returns the number of insertions and deletions for each file
|
||||
// this regex extracts the numbers from the output
|
||||
const regex = /([0-9]+).*([0-9]+)/;
|
||||
|
||||
const [, insertions = 0, deletions = 0] = output.match(regex) || [];
|
||||
|
||||
const isLocaleClean = insertions < 2 && deletions < 2;
|
||||
|
||||
if (isLocaleClean) {
|
||||
return echo('No locale changes, nothing to update, ending process');
|
||||
}
|
||||
|
||||
echo(`Found ${insertions} insertions and ${deletions} deletions in ${templateFile}.`);
|
||||
|
||||
const poFiles = await glob(`${localeDir}/**/amo.po`);
|
||||
|
||||
echo(`Merging ${poFiles.length} translation files.`);
|
||||
|
||||
for await (const poFile of poFiles) {
|
||||
const dir = path.dirname(poFile);
|
||||
const stem = path.basename(poFile, '.po');
|
||||
const tempFile = path.join(dir, `${stem}.po.tmp`);
|
||||
echo(`merging: ${poFile}`);
|
||||
|
||||
try {
|
||||
await $`msgmerge --no-fuzzy-matching -q -o ${tempFile} ${poFile} ${templateFile}`
|
||||
await $`mv ${tempFile} ${poFile}`
|
||||
} catch (error) {
|
||||
await $`rm ${tempFile}`;
|
||||
throw new Error(`Error merging ${poFile}`);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
yarn extract-locales
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/* eslint-disable no-console */
|
||||
require('@babel/register');
|
||||
|
||||
const { globSync } = require('glob');
|
||||
const path = require('path');
|
||||
const shell = require('shelljs');
|
||||
|
||||
const localeDir = path.join(__dirname, '../locale');
|
||||
|
||||
const poFiles = globSync(`${localeDir}/**/amo.po`);
|
||||
const template =
|
||||
path.join(localeDir, `templates/LC_MESSAGES/amo.pot`);
|
||||
|
||||
poFiles.forEach((poFile) => {
|
||||
const dir = path.dirname(poFile);
|
||||
const stem = path.basename(poFile, '.po');
|
||||
const tempFile = path.join(dir, `${stem}.po.tmp`);
|
||||
shell.exec(
|
||||
`msgmerge --no-fuzzy-matching -q -o ${tempFile} ${poFile} ${template}`
|
||||
);
|
||||
shell.mv(tempFile, poFile);
|
||||
});
|
|
@ -0,0 +1,58 @@
|
|||
#! /bin/bash
|
||||
|
||||
# Exit immediately when a command fails.
|
||||
set -e
|
||||
|
||||
# Make sure exit code are respected in a pipeline.
|
||||
set -o pipefail
|
||||
|
||||
# Treat unset variables as an error an exit immediately.
|
||||
set -u
|
||||
|
||||
info() {
|
||||
local message="$1"
|
||||
|
||||
echo ""
|
||||
echo "INFO: $message"
|
||||
echo ""
|
||||
}
|
||||
|
||||
ROBOT_EMAIL="addons-dev-automation+github@mozilla.com"
|
||||
ROBOT_NAME="Mozilla Add-ons Robot"
|
||||
|
||||
# Set git committer/author to the robot.
|
||||
export GIT_AUTHOR_NAME="$ROBOT_NAME"
|
||||
export GIT_AUTHOR_EMAIL="$ROBOT_EMAIL"
|
||||
export GIT_COMMITTER_NAME="$ROBOT_NAME"
|
||||
export GIT_COMMITTER_EMAIL="$ROBOT_EMAIL"
|
||||
|
||||
DATE=$(date -u +%Y-%m-%d)
|
||||
REV=$(git rev-parse --short HEAD)
|
||||
MESSAGE="Extracted l10n messages from $DATE at $REV"
|
||||
DIFF_WITH_ONE_LINE_CHANGE="2 files changed, 2 insertions(+), 2 deletions(-)"
|
||||
|
||||
git_diff_stat=$(git diff --shortstat locale/templates/LC_MESSAGES)
|
||||
|
||||
info "git_diff_stat: $git_diff_stat"
|
||||
|
||||
# IF there are no uncommitted local changes, exit early.
|
||||
if [[ -z "$git_diff_stat" ]] || [[ "$git_diff_stat" == *"$DIFF_WITH_ONE_LINE_CHANGE"* ]]; then
|
||||
info """
|
||||
No substantial changes to l10n strings found. Exiting the process.
|
||||
"""
|
||||
exit 0
|
||||
fi
|
||||
|
||||
info """
|
||||
GIT_AUTHOR_NAME: $GIT_AUTHOR_NAME
|
||||
GIT_AUTHOR_EMAIL: $GIT_AUTHOR_EMAIL
|
||||
GIT_COMMITTER_NAME: $GIT_COMMITTER_NAME
|
||||
GIT_COMMITTER_EMAIL: $GIT_COMMITTER_EMAIL
|
||||
|
||||
This script passes arguments directly to Git commands. We can pass --dry-mode to test this script
|
||||
Without actually committing or pushing. Make sure to only pass arguments supported on both commit and push..
|
||||
ARGS: $@
|
||||
"""
|
||||
|
||||
git commit -am "$MESSAGE" "$@"
|
||||
git push "$@"
|
|
@ -1,100 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Exit immediately when a command fails.
|
||||
set -e
|
||||
# Make sure exit code are respected in a pipeline.
|
||||
set -o pipefail
|
||||
# Treat unset variables as an error an exit immediately.
|
||||
set -u
|
||||
|
||||
INITIAL_GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
GIT_CHANGES=$(git status --porcelain)
|
||||
DIFF_WITH_ONE_LINE_CHANGE="1 file changed, 1 insertion(+), 1 deletion(-)"
|
||||
GIT_REMOTE="${GIT_REMOTE:-git@github.com:mozilla/addons-frontend.git}"
|
||||
LOG_FILE="l10n-extraction.log"
|
||||
|
||||
info() {
|
||||
local message="$1"
|
||||
|
||||
echo ""
|
||||
echo "INFO: $message"
|
||||
echo ""
|
||||
}
|
||||
|
||||
error() {
|
||||
local message="$1"
|
||||
|
||||
echo "ERROR: $message"
|
||||
exit 1
|
||||
}
|
||||
|
||||
run_l10n_extraction() {
|
||||
local branch="amo-locales"
|
||||
|
||||
info "Starting l10n extraction"
|
||||
|
||||
# Detect local (uncommitted) changes.
|
||||
if [[ ! -z "$GIT_CHANGES" ]]; then
|
||||
error "You have local changes, therefore this script cannot continue."
|
||||
fi
|
||||
|
||||
# Switch to the `master` branch if we are not on it already.
|
||||
if [[ "$INITIAL_GIT_BRANCH" != "master" ]]; then
|
||||
git checkout master
|
||||
fi
|
||||
|
||||
# Make sure the 'master' branch is up-to-date.
|
||||
git pull "$GIT_REMOTE" master
|
||||
|
||||
# Update dependencies
|
||||
yarn >> "$LOG_FILE" 2>&1
|
||||
|
||||
# Ensure the branch to extract the locales is clean.
|
||||
if [[ $(git branch --list "$branch") ]]; then
|
||||
info "Deleting branch '$branch' because it already exists"
|
||||
git branch -D "$branch"
|
||||
fi
|
||||
|
||||
info "Creating and switching to branch '$branch'"
|
||||
git checkout -b "$branch"
|
||||
|
||||
info "Extracting locales (this can take several minutes)"
|
||||
bin/extract-locales >> "$LOG_FILE" 2>&1
|
||||
|
||||
local git_diff_stat
|
||||
git_diff_stat=$(git diff --shortstat)
|
||||
|
||||
if [[ -z "$git_diff_stat" ]] || [[ "$git_diff_stat" == *"$DIFF_WITH_ONE_LINE_CHANGE"* ]]; then
|
||||
info "No locale changes, nothing to update, ending process"
|
||||
git checkout -- .
|
||||
git checkout "$INITIAL_GIT_BRANCH"
|
||||
git branch -d "$branch"
|
||||
return
|
||||
fi
|
||||
|
||||
git commit -a -m "Extract locales" --no-verify
|
||||
|
||||
info "Merging locales"
|
||||
bin/merge-locales >> "$LOG_FILE" 2>&1
|
||||
|
||||
git commit -a -m "Merge locales" --no-verify
|
||||
|
||||
# We use force-push in case the branch already exists.
|
||||
git push -f "$GIT_REMOTE" "$branch"
|
||||
|
||||
git checkout "$INITIAL_GIT_BRANCH"
|
||||
git branch -D "$branch"
|
||||
|
||||
echo ""
|
||||
echo "----------------------------------------------------------------------"
|
||||
echo ""
|
||||
echo " You can now open the link below to submit your Pull Request:"
|
||||
echo " https://github.com/mozilla/addons-frontend/pull/new/$branch"
|
||||
echo ""
|
||||
echo "----------------------------------------------------------------------"
|
||||
}
|
||||
|
||||
# Clear log file
|
||||
echo "" > "$LOG_FILE"
|
||||
|
||||
run_l10n_extraction
|
87
docs/i18n.md
87
docs/i18n.md
|
@ -20,94 +20,19 @@ NODE_PATH='./:./src' bin/create-locales
|
|||
|
||||
## Updating locales
|
||||
|
||||
TL;DR: run the following script from the `master` branch: `./bin/run-l10n-extraction`
|
||||
Locales are updated automatically as a part of our CI. On every push to master `yarn extract-locales` is run which extracts locale strings from our codebase, merges any changes to the source language files and commits the changes.
|
||||
|
||||
### The long story
|
||||
You can run this command manually on your local environment any time to check the output strings.
|
||||
|
||||
Once a week right after the forthcoming release [is tagged](http://addons.readthedocs.io/en/latest/server/push-duty.html), the locales for each app must be generated.
|
||||
Github actions internally prevent infinite loops by default.
|
||||
|
||||
This is a semi-automated process: a team member must create a pull request with the following commits:
|
||||
|
||||
1. A commit containing the extraction of newly added strings
|
||||
2. A commit containing a merge of localizations
|
||||
|
||||
Each one of these steps are detailed in the sections below. Let's begin...
|
||||
|
||||
#### Extracting newly added strings
|
||||
|
||||
Start the process by creating a git branch and extracting the locales.
|
||||
|
||||
```
|
||||
git checkout master
|
||||
git pull
|
||||
git checkout -b amo-locales
|
||||
bin/extract-locales
|
||||
```
|
||||
|
||||
This extracts all strings wrapped with `i18n.gettext()` or any other function supported by [Jed][jed] (the library we use in JavaScript to carry out replacements for the string keys in the current locale).
|
||||
|
||||
The strings are extracted using a babel plugin via webpack. Extracted strings are added to a pot template file. This file is used to seed the po for each locale with the strings needing translating when merging locales.
|
||||
|
||||
Run `git diff` to see what the extraction did. **If no strings were updated then you do not have to continue creating the pull request. You can revert the changes made to the `pot` timestamp.** Here is an example of a diff where no strings were changed. It just shows a single change to the timestamp:
|
||||
|
||||
```diff
|
||||
diff --git a/locale/templates/LC_MESSAGES/amo.pot b/locale/templates/LC_MESSAGES/amo.pot
|
||||
index 31e113f2..c7da4e34 100644
|
||||
--- a/locale/templates/LC_MESSAGES/amo.pot
|
||||
+++ b/locale/templates/LC_MESSAGES/amo.pot
|
||||
@@ -2,7 +2,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: amo\n"
|
||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
|
||||
-"POT-Creation-Date: 2017-06-08 14:01+0000\n"
|
||||
+"POT-Creation-Date: 2017-06-08 14:43+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
```
|
||||
|
||||
When the application is under active development it's more likely that you will see a diff containing new strings or at least strings that have shifted to different line numbers in the source. If so, commit your change and continue to the next step:
|
||||
|
||||
```
|
||||
git commit -a -m "Extract AMO locales"
|
||||
```
|
||||
|
||||
#### Merging locale files
|
||||
|
||||
After extracting new strings, you have to merge them into the existing locale files. Do this in your branch and commit:
|
||||
|
||||
```
|
||||
bin/merge-locales
|
||||
```
|
||||
|
||||
Keep an eye out for [fuzzy strings](https://www.gnu.org/software/gettext/manual/html_node/Fuzzy-Entries.html) by running `git diff` and searching for a comment that looks like `# fuzzy`. This comment means the localization may not exactly match the source text; a localizer needs to review it. As per our configuration, the application will not display fuzzy translations. These strings will fall back to English.
|
||||
|
||||
In some rare cases you may wish to remove the `fuzzy` marker to prevent falling back to English. Discuss it with a team member before removing `fuzzy` markers.
|
||||
|
||||
Commit and continue to the next step:
|
||||
|
||||
```
|
||||
git commit -a -m "Merged AMO locales"
|
||||
```
|
||||
|
||||
#### Finalizing the extract/merge process
|
||||
|
||||
Now that you have extracted and merged locales for one application, it's time to create a pull request for your branch. For example:
|
||||
|
||||
```
|
||||
git push origin amo-locales
|
||||
```
|
||||
|
||||
If the pull request passes all of our CI tests it is likely good to merge. You don't need to ask for a review unless you're unsure of something because often locale updates will be thousands of lines of minor diffs that can't be reasonably reviewed by a human. 🙂 If the pull request passes all of our CI tests it is likely good to merge.
|
||||
|
||||
#### Building the JS locale files
|
||||
### Building the JS locale files
|
||||
|
||||
This command creates the JSON files which are then built into JS bundles by webpack when the build step is run. This happens automatically as part of the deployment process.
|
||||
|
||||
Since dist files are created when needed you only need to build and commit the JSON to the repo.
|
||||
Since dist files are created when needed you only need to build and commit the JSON to the repo:
|
||||
|
||||
```
|
||||
# build the JSON.
|
||||
```bash
|
||||
bin/build-locales
|
||||
```
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@
|
|||
}
|
||||
},
|
||||
"extract-locales": {
|
||||
"command": "webpack --progress --color --config webpack.l10n.config.babel.js",
|
||||
"command": "zx ./bin/extract-locales",
|
||||
"env": {
|
||||
"NODE_ENV": "production",
|
||||
"NODE_ICU_DATA": "./node_modules/full-icu",
|
||||
|
@ -253,6 +253,7 @@
|
|||
"webpack-isomorphic-tools": "4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.23.4",
|
||||
"@babel/core": "^7.24.7",
|
||||
"@babel/eslint-parser": "^7.24.7",
|
||||
"@babel/preset-env": "^7.24.7",
|
||||
|
@ -329,7 +330,8 @@
|
|||
"webpack-cli": "^4.0.0",
|
||||
"webpack-dev-middleware": "^6.1.2",
|
||||
"webpack-hot-middleware": "^2.26.1",
|
||||
"webpack-subresource-integrity": "5.1.0"
|
||||
"webpack-subresource-integrity": "5.1.0",
|
||||
"zx": "^8.1.4"
|
||||
},
|
||||
"bundlewatch": [
|
||||
{
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/* eslint-disable no-console, import/no-extraneous-dependencies */
|
||||
import chalk from 'chalk';
|
||||
import webpack from 'webpack';
|
||||
|
||||
import { getRules } from './webpack-common';
|
||||
import webpackConfig from './webpack.prod.config.babel';
|
||||
import { WEBPACK_ENTRYPOINT } from './src/amo/constants';
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.log(chalk.red('This should be run with NODE_ENV="production"'));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const babelConfig = require('./babel.config');
|
||||
|
||||
const babelPlugins = babelConfig.plugins || [];
|
||||
|
||||
// Create UTC creation date in the correct format.
|
||||
const potCreationDate = new Date()
|
||||
.toISOString()
|
||||
.replace('T', ' ')
|
||||
.replace(/:\d{2}.\d{3}Z/, '+0000');
|
||||
|
||||
const babelL10nPlugins = [
|
||||
[
|
||||
'module:babel-gettext-extractor',
|
||||
{
|
||||
headers: {
|
||||
'Project-Id-Version': 'amo',
|
||||
'Report-Msgid-Bugs-To': 'EMAIL@ADDRESS',
|
||||
'POT-Creation-Date': potCreationDate,
|
||||
'PO-Revision-Date': 'YEAR-MO-DA HO:MI+ZONE',
|
||||
'Last-Translator': 'FULL NAME <EMAIL@ADDRESS>',
|
||||
'Language-Team': 'LANGUAGE <LL@li.org>',
|
||||
'MIME-Version': '1.0',
|
||||
'Content-Type': 'text/plain; charset=utf-8',
|
||||
'Content-Transfer-Encoding': '8bit',
|
||||
'plural-forms': 'nplurals=2; plural=(n!=1);',
|
||||
},
|
||||
functionNames: {
|
||||
gettext: ['msgid'],
|
||||
dgettext: ['domain', 'msgid'],
|
||||
ngettext: ['msgid', 'msgid_plural', 'count'],
|
||||
dngettext: ['domain', 'msgid', 'msgid_plural', 'count'],
|
||||
pgettext: ['msgctxt', 'msgid'],
|
||||
dpgettext: ['domain', 'msgctxt', 'msgid'],
|
||||
npgettext: ['msgctxt', 'msgid', 'msgid_plural', 'count'],
|
||||
dnpgettext: ['domain', 'msgctxt', 'msgid', 'msgid_plural', 'count'],
|
||||
},
|
||||
fileName: `locale/templates/LC_MESSAGES/amo.pot`,
|
||||
baseDirectory: process.cwd(),
|
||||
stripTemplateLiteralIndent: true,
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
const babelOptions = {
|
||||
...babelConfig,
|
||||
plugins: babelPlugins.concat(babelL10nPlugins),
|
||||
};
|
||||
|
||||
export default {
|
||||
...webpackConfig,
|
||||
entry: { [WEBPACK_ENTRYPOINT]: 'amo/client' },
|
||||
module: {
|
||||
rules: getRules({ babelOptions }),
|
||||
},
|
||||
plugins: [
|
||||
// Don't generate modules for locale files.
|
||||
new webpack.IgnorePlugin({
|
||||
resourceRegExp: /locale\/.*\/amo\.js$/,
|
||||
}),
|
||||
...webpackConfig.plugins,
|
||||
],
|
||||
};
|
103
yarn.lock
103
yarn.lock
|
@ -20,6 +20,22 @@
|
|||
"@jridgewell/gen-mapping" "^0.1.0"
|
||||
"@jridgewell/trace-mapping" "^0.3.9"
|
||||
|
||||
"@babel/cli@^7.23.4":
|
||||
version "7.24.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.24.8.tgz#79eaa55a69c77cafbea3e87537fd1df5a5a2edf8"
|
||||
integrity sha512-isdp+G6DpRyKc+3Gqxy2rjzgF7Zj9K0mzLNnxz+E/fgeag8qT3vVulX4gY9dGO1q0y+0lUv6V3a+uhUzMzrwXg==
|
||||
dependencies:
|
||||
"@jridgewell/trace-mapping" "^0.3.25"
|
||||
commander "^6.2.0"
|
||||
convert-source-map "^2.0.0"
|
||||
fs-readdir-recursive "^1.1.0"
|
||||
glob "^7.2.0"
|
||||
make-dir "^2.1.0"
|
||||
slash "^2.0.0"
|
||||
optionalDependencies:
|
||||
"@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3"
|
||||
chokidar "^3.4.0"
|
||||
|
||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.24.7":
|
||||
version "7.24.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465"
|
||||
|
@ -1479,6 +1495,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@mozilla-protocol/tokens/-/tokens-5.0.5.tgz#92bc2e5fe61e9706c95f9f3546bed556526dba2a"
|
||||
integrity sha512-VJ2fYJs09m0x1yWHVGhbqp2ZBOfEZy8vbOGYqNXCVrsYriLZp20CdKJq3+jj/chEVz+S5b0wdIOKkLqvgqIr/A==
|
||||
|
||||
"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3":
|
||||
version "2.1.8-no-fsevents.3"
|
||||
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b"
|
||||
integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==
|
||||
|
||||
"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
|
||||
version "5.1.1-v1"
|
||||
resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129"
|
||||
|
@ -1799,6 +1820,14 @@
|
|||
"@types/qs" "*"
|
||||
"@types/serve-static" "*"
|
||||
|
||||
"@types/fs-extra@>=11":
|
||||
version "11.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.4.tgz#e16a863bb8843fba8c5004362b5a73e17becca45"
|
||||
integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==
|
||||
dependencies:
|
||||
"@types/jsonfile" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/graceful-fs@^4.1.3":
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15"
|
||||
|
@ -1857,6 +1886,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
|
||||
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
|
||||
|
||||
"@types/jsonfile@*":
|
||||
version "6.1.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/jsonfile/-/jsonfile-6.1.4.tgz#614afec1a1164e7d670b4a7ad64df3e7beb7b702"
|
||||
integrity sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/mime@*":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10"
|
||||
|
@ -1872,6 +1908,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.17.tgz#5c009e1d9c38f4a2a9d45c0b0c493fe6cdb4bcb5"
|
||||
integrity sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==
|
||||
|
||||
"@types/node@>=20":
|
||||
version "20.14.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.10.tgz#a1a218290f1b6428682e3af044785e5874db469a"
|
||||
integrity sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==
|
||||
dependencies:
|
||||
undici-types "~5.26.4"
|
||||
|
||||
"@types/normalize-package-data@^2.4.0":
|
||||
version "2.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
|
||||
|
@ -3114,7 +3157,7 @@ chokidar-cli@^3.0.0:
|
|||
lodash.throttle "^4.1.1"
|
||||
yargs "^13.3.0"
|
||||
|
||||
chokidar@3.6.0, chokidar@^3.5.2:
|
||||
chokidar@3.6.0, chokidar@^3.4.0, chokidar@^3.5.2:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
|
||||
integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
|
||||
|
@ -3292,7 +3335,7 @@ commander@^5.0.0:
|
|||
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
|
||||
integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
|
||||
|
||||
commander@^6.0.0:
|
||||
commander@^6.0.0, commander@^6.2.0:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
|
||||
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
||||
|
@ -5173,6 +5216,11 @@ fs-monkey@^1.0.3:
|
|||
resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3"
|
||||
integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==
|
||||
|
||||
fs-readdir-recursive@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27"
|
||||
integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==
|
||||
|
||||
fs.realpath@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
|
@ -5355,7 +5403,7 @@ glob@^10.3.7, glob@^10.4.2:
|
|||
package-json-from-dist "^1.0.0"
|
||||
path-scurry "^1.11.1"
|
||||
|
||||
glob@^7.0.0, glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
|
||||
glob@^7.0.0, glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.2.0:
|
||||
version "7.2.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
|
||||
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
||||
|
@ -9805,6 +9853,11 @@ sisteransi@^1.0.5:
|
|||
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
|
||||
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
|
||||
|
||||
slash@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
|
||||
integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
|
||||
|
||||
slash@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
|
||||
|
@ -10015,16 +10068,7 @@ string-natural-compare@3.0.1:
|
|||
resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
|
||||
integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
|
||||
|
||||
"string-width-cjs@npm:string-width@^4.2.0":
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
|
@ -10138,7 +10182,7 @@ string_decoder@~1.1.1:
|
|||
dependencies:
|
||||
safe-buffer "~5.1.0"
|
||||
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
|
||||
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
|
@ -10152,13 +10196,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
|
|||
dependencies:
|
||||
ansi-regex "^4.1.0"
|
||||
|
||||
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
|
||||
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-ansi@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
|
||||
|
@ -10815,6 +10852,11 @@ undefsafe@^2.0.5:
|
|||
resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
|
||||
integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==
|
||||
|
||||
undici-types@~5.26.4:
|
||||
version "5.26.5"
|
||||
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
|
||||
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
|
||||
|
||||
unicode-canonical-property-names-ecmascript@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
|
||||
|
@ -11340,7 +11382,7 @@ wordwrap@0.0.2:
|
|||
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f"
|
||||
integrity sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q==
|
||||
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
|
||||
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
|
@ -11358,15 +11400,6 @@ wrap-ansi@^5.1.0:
|
|||
string-width "^3.0.0"
|
||||
strip-ansi "^5.0.0"
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrap-ansi@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
|
||||
|
@ -11511,3 +11544,11 @@ yocto-queue@^1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
|
||||
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
|
||||
|
||||
zx@^8.1.4:
|
||||
version "8.1.4"
|
||||
resolved "https://registry.yarnpkg.com/zx/-/zx-8.1.4.tgz#19bcd543fab34d01d7d4174a314b33cde9115a17"
|
||||
integrity sha512-QFDYYpnzdpRiJ3dL2102Cw26FpXpWshW4QLTGxiYfIcwdAqg084jRCkK/kuP/NOSkxOjydRwNFG81qzA5r1a6w==
|
||||
optionalDependencies:
|
||||
"@types/fs-extra" ">=11"
|
||||
"@types/node" ">=20"
|
||||
|
|
Загрузка…
Ссылка в новой задаче