diff --git a/.github/ISSUE_TEMPLATE/2-enhancement.yml b/.github/ISSUE_TEMPLATE/2-enhancement.yml index 3c7637cea8c..19383e15c27 100644 --- a/.github/ISSUE_TEMPLATE/2-enhancement.yml +++ b/.github/ISSUE_TEMPLATE/2-enhancement.yml @@ -1,5 +1,5 @@ name: ✨ Enhancement Request -description: If you have an idea to improve an existing feature in core or need something for development (such as a new hook) please let us know or submit a Pull Request! +description: If you have an idea to improve existing functionality in core or need something for development (such as a new hook) please let us know or submit a Pull Request! title: "[Enhancement]: " labels: ["type: enhancement", "status: awaiting triage"] body: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index dae2134fb39..f8f1bcc6e24 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,8 @@ blank_issues_enabled: true contact_links: + - name: Feature Requests + url: https://woocommerce.com/feature-requests/woocommerce/ + about: If you have an idea for a new feature that you wished existed in WooCommerce, take a look at our Feature Requests and vote, or open a new Feature Request yourself! - name: 🔒 Security issue url: https://hackerone.com/automattic/ about: For security reasons, please report all security issues via HackerOne. If the issue is valid, a bug bounty will be paid out to you. Please disclose responsibly and not via GitHub (which allows for exploiting issues in the wild before the patch is released). diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 0ff2173615b..eb4fdbc5e7f 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -9,6 +9,8 @@ ### Changes proposed in this Pull Request: + + Closes # . @@ -25,4 +27,4 @@ Using the [WooCommerce Testing Instructions Guide](https://github.com/woocommerc 2. 3. - \ No newline at end of file + diff --git a/.github/actions/tests/run-api-tests/action.yml b/.github/actions/tests/run-api-tests/action.yml index 598a67a0d5f..f569e12afcc 100644 --- a/.github/actions/tests/run-api-tests/action.yml +++ b/.github/actions/tests/run-api-tests/action.yml @@ -9,6 +9,11 @@ inputs: tests: description: Specific tests to run, separated by single whitespace. See https://playwright.dev/docs/test-cli +outputs: + result: + description: Whether the test passed or failed. + value: ${{ steps.run-api-tests.conclusion }} + runs: using: composite steps: diff --git a/.github/actions/tests/run-e2e-tests/action.yml b/.github/actions/tests/run-e2e-tests/action.yml index ab04b6764c4..c2083543476 100644 --- a/.github/actions/tests/run-e2e-tests/action.yml +++ b/.github/actions/tests/run-e2e-tests/action.yml @@ -12,6 +12,11 @@ inputs: description: The Playwright configuration file to use. default: playwright.config.js +outputs: + result: + description: Whether the test passed or failed. + value: ${{ steps.run-e2e-tests.conclusion }} + runs: using: composite steps: diff --git a/.github/actions/tests/slack-summary-on-release/slack-blocks/action.yml b/.github/actions/tests/slack-summary-on-release/slack-blocks/action.yml new file mode 100644 index 00000000000..b98bd4d9aca --- /dev/null +++ b/.github/actions/tests/slack-summary-on-release/slack-blocks/action.yml @@ -0,0 +1,59 @@ +name: Compose a Slack block for release tests +description: Create a Slack block that shows the API and E2E test results from one of the release tests, and upload it as an artifact. +permissions: {} + +inputs: + test-name: + required: true + api-result: + required: true + type: choice + default: skipped + options: + - success + - failure + - cancelled + - skipped + e2e-result: + required: true + type: choice + default: skipped + options: + - success + - failure + - cancelled + - skipped + env-slug: + required: true + release-version: + required: true + +runs: + using: composite + steps: + - name: Create context block as a JSON object + id: generate-json + uses: actions/github-script@v6 + with: + script: | + const script = require('./.github/actions/tests/slack-summary-on-release/slack-blocks/scripts/create-result-block'); + return script(); + env: + API_RESULT: ${{ inputs.api-result }} + E2E_RESULT: ${{ inputs.e2e-result }} + ENV_SLUG: ${{ inputs.env-slug }} + TEST_NAME: ${{ inputs.test-name }} + RELEASE_VERSION: ${{ inputs.release-version }} + + - name: Write JSON file + working-directory: /tmp + shell: bash + env: + CONTEXT_JSON: ${{ toJSON(steps.generate-json.outputs.result) }} + run: echo ${{ env.CONTEXT_JSON }} > "${{ inputs.test-name }}.json" + + - name: Upload JSON file as artifact + uses: actions/upload-artifact@v3 + with: + name: ${{ env.SLACK_BLOCKS_ARTIFACT }} + path: /tmp/${{ inputs.test-name }}.json diff --git a/.github/actions/tests/slack-summary-on-release/slack-blocks/scripts/create-result-block/index.js b/.github/actions/tests/slack-summary-on-release/slack-blocks/scripts/create-result-block/index.js new file mode 100644 index 00000000000..3e0e566d7ce --- /dev/null +++ b/.github/actions/tests/slack-summary-on-release/slack-blocks/scripts/create-result-block/index.js @@ -0,0 +1,31 @@ +module.exports = () => { + const { API_RESULT, E2E_RESULT, ENV_SLUG, TEST_NAME, RELEASE_VERSION } = + process.env; + const { setElementText } = require( './utils' ); + + const apiLinkText = setElementText( { + testType: 'API', + result: API_RESULT, + envSlug: ENV_SLUG, + releaseVersion: RELEASE_VERSION, + } ); + const e2eLinkText = setElementText( { + testType: 'E2E', + result: E2E_RESULT, + envSlug: ENV_SLUG, + releaseVersion: RELEASE_VERSION, + } ); + const elementText = `*${ TEST_NAME }*\n ${ apiLinkText } ${ e2eLinkText }`; + + const contextBlock = { + type: 'context', + elements: [ + { + type: 'mrkdwn', + text: elementText, + }, + ], + }; + + return contextBlock; +}; diff --git a/.github/actions/tests/slack-summary-on-release/slack-blocks/scripts/create-result-block/utils/index.js b/.github/actions/tests/slack-summary-on-release/slack-blocks/scripts/create-result-block/utils/index.js new file mode 100644 index 00000000000..ebf13b1a2e1 --- /dev/null +++ b/.github/actions/tests/slack-summary-on-release/slack-blocks/scripts/create-result-block/utils/index.js @@ -0,0 +1,5 @@ +const { setElementText } = require( './set-element-text' ); + +module.exports = { + setElementText, +}; diff --git a/.github/actions/tests/slack-summary-on-release/slack-blocks/scripts/create-result-block/utils/select-emoji.js b/.github/actions/tests/slack-summary-on-release/slack-blocks/scripts/create-result-block/utils/select-emoji.js new file mode 100644 index 00000000000..a9ba564fe09 --- /dev/null +++ b/.github/actions/tests/slack-summary-on-release/slack-blocks/scripts/create-result-block/utils/select-emoji.js @@ -0,0 +1,26 @@ +const emojis = { + PASSED: ':workflow-passed:', + FAILED: ':workflow-failed:', + SKIPPED: ':workflow-skipped:', + CANCELLED: ':workflow-cancelled:', + UNKNOWN: ':grey_question:', +}; + +const selectEmoji = ( result ) => { + switch ( result ) { + case 'success': + return emojis.PASSED; + case 'failure': + return emojis.FAILED; + case 'skipped': + return emojis.SKIPPED; + case 'cancelled': + return emojis.CANCELLED; + default: + return emojis.UNKNOWN; + } +}; + +module.exports = { + selectEmoji, +}; diff --git a/.github/actions/tests/slack-summary-on-release/slack-blocks/scripts/create-result-block/utils/set-element-text.js b/.github/actions/tests/slack-summary-on-release/slack-blocks/scripts/create-result-block/utils/set-element-text.js new file mode 100644 index 00000000000..6ea210d4259 --- /dev/null +++ b/.github/actions/tests/slack-summary-on-release/slack-blocks/scripts/create-result-block/utils/set-element-text.js @@ -0,0 +1,12 @@ +const setElementText = ( { testType, result, envSlug, releaseVersion } ) => { + const { selectEmoji } = require( './select-emoji' ); + const allureReportURL = `https://woocommerce.github.io/woocommerce-test-reports/release/${ releaseVersion }/${ envSlug }/${ testType.toLowerCase() }`; + const emoji = selectEmoji( result ); + const textValue = `<${ allureReportURL }|${ testType.toUpperCase() } ${ emoji }>`; + + return textValue; +}; + +module.exports = { + setElementText, +}; diff --git a/.github/actions/tests/slack-summary-on-release/slack-payload/action.yml b/.github/actions/tests/slack-summary-on-release/slack-payload/action.yml new file mode 100644 index 00000000000..a7cf1f3df7b --- /dev/null +++ b/.github/actions/tests/slack-summary-on-release/slack-payload/action.yml @@ -0,0 +1,27 @@ +name: Combine all Slack blocks +description: Combine all Slack blocks to construct the payload for the Slack GitHub action +permissions: {} + +inputs: + release-version: + required: true + blocks-dir: + require: true + +outputs: + payload: + value: ${{ steps.payload.outputs.result }} + +runs: + using: composite + steps: + - name: Construct payload from all blocks + id: payload + uses: actions/github-script@v6 + env: + RELEASE_VERSION: ${{ inputs.release-version }} + BLOCKS_DIR: ${{ inputs.blocks-dir }} + with: + script: | + const script = require('./.github/actions/tests/slack-summary-on-release/slack-payload/scripts/construct-payload'); + return script(); diff --git a/.github/actions/tests/slack-summary-on-release/slack-payload/scripts/construct-payload/index.js b/.github/actions/tests/slack-summary-on-release/slack-payload/scripts/construct-payload/index.js new file mode 100644 index 00000000000..a63633b314f --- /dev/null +++ b/.github/actions/tests/slack-summary-on-release/slack-payload/scripts/construct-payload/index.js @@ -0,0 +1,36 @@ +module.exports = () => { + const { RELEASE_VERSION, BLOCKS_DIR } = process.env; + const { + filterContextBlocks, + readContextBlocksFromJsonFiles, + } = require( './utils' ); + + const headerText = `Test summary for ${ RELEASE_VERSION }`; + const headerBlock = { + type: 'header', + text: { + type: 'plain_text', + text: headerText, + emoji: true, + }, + }; + + const blocks_all = readContextBlocksFromJsonFiles( BLOCKS_DIR ); + const blocks_wcUpdate = filterContextBlocks( blocks_all, 'WC Update' ); + const blocks_wpVersions = filterContextBlocks( blocks_all, 'WP Latest' ); + const blocks_phpVersions = filterContextBlocks( blocks_all, 'PHP' ); + const blocks_plugins = filterContextBlocks( blocks_all, 'With' ); + + const blocksPayload = [ headerBlock ] + .concat( blocks_wcUpdate ) + .concat( blocks_wpVersions ) + .concat( blocks_phpVersions ) + .concat( blocks_plugins ); + + const payload = { + text: headerText, + blocks: blocksPayload, + }; + + return payload; +}; diff --git a/.github/actions/tests/slack-summary-on-release/slack-payload/scripts/construct-payload/utils/get-context-blocks.js b/.github/actions/tests/slack-summary-on-release/slack-payload/scripts/construct-payload/utils/get-context-blocks.js new file mode 100644 index 00000000000..338e45bafad --- /dev/null +++ b/.github/actions/tests/slack-summary-on-release/slack-payload/scripts/construct-payload/utils/get-context-blocks.js @@ -0,0 +1,42 @@ +const fs = require( 'fs' ); +const path = require( 'path' ); + +const readContextBlocksFromJsonFiles = ( blocksDir ) => { + const jsonsDir = path.resolve( blocksDir ); + const jsons = fs.readdirSync( jsonsDir ); + + let contextBlocks = []; + + for ( const json of jsons ) { + const jsonPath = path.resolve( jsonsDir, json ); + const contextBlock = require( jsonPath ); + + contextBlocks.push( contextBlock ); + } + + return contextBlocks; +}; + +const filterContextBlocks = ( blocks, testName ) => { + const divider = { + type: 'divider', + }; + + let filteredBlocks = []; + + const matchingBlocks = blocks.filter( ( { elements } ) => + elements[ 0 ].text.includes( testName ) + ); + + matchingBlocks.forEach( ( block ) => { + filteredBlocks.push( block ); + filteredBlocks.push( divider ); + } ); + + return filteredBlocks; +}; + +module.exports = { + filterContextBlocks, + readContextBlocksFromJsonFiles, +}; diff --git a/.github/actions/tests/slack-summary-on-release/slack-payload/scripts/construct-payload/utils/index.js b/.github/actions/tests/slack-summary-on-release/slack-payload/scripts/construct-payload/utils/index.js new file mode 100644 index 00000000000..dc773de7eb9 --- /dev/null +++ b/.github/actions/tests/slack-summary-on-release/slack-payload/scripts/construct-payload/utils/index.js @@ -0,0 +1,6 @@ +const { + filterContextBlocks, + readContextBlocksFromJsonFiles, +} = require( './get-context-blocks' ); + +module.exports = { filterContextBlocks, readContextBlocksFromJsonFiles }; diff --git a/.github/workflows/changelog-auto-add.yml b/.github/workflows/changelog-auto-add.yml new file mode 100644 index 00000000000..1afd60511b3 --- /dev/null +++ b/.github/workflows/changelog-auto-add.yml @@ -0,0 +1,10 @@ +name: 'Changelog Auto Add' +on: workflow_dispatch + +jobs: + hello-world: + name: 'Hello World' + runs-on: ubuntu-20.04 + steps: + - name: Hello + run: echo "Hello World" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e58e5508c2..c9785c61b61 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,23 +58,24 @@ jobs: working-directory: plugins/woocommerce run: pnpm run build:feature-config - - name: Add PHP8 Compatibility. - run: | - if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then - cd plugins/woocommerce - curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip - unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip - composer bin phpunit config --unset platform - composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}' - composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs - rm -rf ./vendor/phpunit/ - composer dump-autoload - fi + - id: parseMatrix + name: Parse Matrix Variables + uses: actions/github-script@v6 + with: + script: | + const parseWPVersion = require( './.github/workflows/scripts/parse-wp-version' ); + parseWPVersion( '${{ matrix.wp }}' ).then( ( version ) => { + core.setOutput( 'wpVersion', version ); + } ); - - name: Init DB and WP - working-directory: plugins/woocommerce - run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }} + - name: Prepare Testing Environment + env: + WP_ENV_CORE: ${{ steps.parseMatrix.outputs.wpVersion }} + WP_ENV_PHP_VERSION: ${{ matrix.php }} + run: pnpm --filter=woocommerce env:test - - name: Run tests - working-directory: plugins/woocommerce - run: pnpm run test --color + - name: Run Tests + env: + WP_ENV_CORE: ${{ steps.parseMatrix.outputs.wpVersion }} + WP_ENV_PHP_VERSION: ${{ matrix.php }} + run: pnpm --filter=woocommerce test:unit:env diff --git a/.github/workflows/pr-build-and-e2e-tests.yml b/.github/workflows/pr-build-and-e2e-tests.yml index 0ddad0d4617..bcb64f055a0 100644 --- a/.github/workflows/pr-build-and-e2e-tests.yml +++ b/.github/workflows/pr-build-and-e2e-tests.yml @@ -13,7 +13,6 @@ permissions: {} jobs: e2e-tests-run: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }} name: Runs E2E tests. runs-on: ubuntu-20.04 permissions: @@ -85,7 +84,6 @@ jobs: api-tests-run: name: Runs API tests. - if: github.event.pull_request.user.login != 'github-actions[bot]' runs-on: ubuntu-20.04 permissions: contents: read diff --git a/.github/workflows/pr-code-coverage.yml b/.github/workflows/pr-code-coverage.yml deleted file mode 100644 index 3df9ef1d464..00000000000 --- a/.github/workflows/pr-code-coverage.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Run code coverage on PR -on: - pull_request: - paths-ignore: - - '**/changelog/**' - workflow_dispatch: -defaults: - run: - shell: bash -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: {} - -jobs: - test: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }} - name: Code coverage (PHP 7.4, WP Latest) - timeout-minutes: 30 - runs-on: ubuntu-20.04 - permissions: - contents: read - services: - database: - image: mysql:5.6 - env: - MYSQL_ROOT_PASSWORD: root - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 100 - - - name: Setup WooCommerce Monorepo - uses: ./.github/actions/setup-woocommerce-monorepo - - - name: Tool versions - run: | - php --version - composer --version - - - name: Build Admin feature config - working-directory: plugins/woocommerce - run: pnpm run build:feature-config - - - name: Init DB and WP - working-directory: plugins/woocommerce - run: bash tests/bin/install.sh woo_test root root 127.0.0.1 latest - - - name: Run unit tests with code coverage. Allow to fail. - working-directory: plugins/woocommerce - run: | - RUN_CODE_COVERAGE=1 bash tests/bin/phpunit.sh - exit 0 - - - name: Send code coverage to Codecov. - run: | - bash <(curl -s https://codecov.io/bash) diff --git a/.github/workflows/pr-code-sniff.yml b/.github/workflows/pr-code-sniff.yml index 6026b2cd18e..7578e652db6 100644 --- a/.github/workflows/pr-code-sniff.yml +++ b/.github/workflows/pr-code-sniff.yml @@ -16,7 +16,6 @@ permissions: {} jobs: test: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }} name: Code sniff (PHP 7.4, WP Latest) timeout-minutes: 15 runs-on: ubuntu-20.04 diff --git a/.github/workflows/pr-highlight-changes.yml b/.github/workflows/pr-highlight-changes.yml index a43759ca526..e5f0e8bd563 100644 --- a/.github/workflows/pr-highlight-changes.yml +++ b/.github/workflows/pr-highlight-changes.yml @@ -8,7 +8,7 @@ permissions: {} jobs: analyze: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }} + if: ${{ github.event.pull_request.user.login != 'github-actions[bot]' }} name: Check pull request changes to highlight runs-on: ubuntu-20.04 permissions: diff --git a/.github/workflows/pr-lint-monorepo.yml b/.github/workflows/pr-lint-monorepo.yml index b456ecb82ea..684e8ca1ebd 100644 --- a/.github/workflows/pr-lint-monorepo.yml +++ b/.github/workflows/pr-lint-monorepo.yml @@ -11,7 +11,6 @@ permissions: {} jobs: changelogger_used: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }} name: Changelogger use runs-on: ubuntu-20.04 permissions: diff --git a/.github/workflows/pr-lint-test-js.yml b/.github/workflows/pr-lint-test-js.yml index 9cbfcbd3659..972367f0abd 100644 --- a/.github/workflows/pr-lint-test-js.yml +++ b/.github/workflows/pr-lint-test-js.yml @@ -12,7 +12,6 @@ permissions: {} jobs: lint-test-js: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }} name: Lint and Test JS runs-on: ubuntu-20.04 permissions: diff --git a/.github/workflows/pr-unit-tests.yml b/.github/workflows/pr-unit-tests.yml index cda4e690a7d..3afbd0be4b7 100644 --- a/.github/workflows/pr-unit-tests.yml +++ b/.github/workflows/pr-unit-tests.yml @@ -14,7 +14,7 @@ permissions: {} jobs: test: - if: ${{ github.event_name != 'pull_request' || github.event.pull_request.user.login != 'github-actions[bot]' }} + if: ${{ github.event.pull_request.user.login != 'github-actions[bot]' }} name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} ${{ matrix.hpos && 'HPOS' || '' }} timeout-minutes: 30 runs-on: ubuntu-20.04 @@ -52,15 +52,24 @@ jobs: with: php-version: ${{ matrix.php }} - - name: Tool versions - run: | - php --version - composer --version + - id: parseMatrix + name: Parse Matrix Variables + uses: actions/github-script@v6 + with: + script: | + const parseWPVersion = require( './.github/workflows/scripts/parse-wp-version' ); + parseWPVersion( '${{ matrix.wp }}' ).then( ( version ) => { + core.setOutput( 'wpVersion', version ); + } ); - - name: Init DB and WP - working-directory: plugins/woocommerce - run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }} + - name: Prepare Testing Environment + env: + WP_ENV_CORE: ${{ steps.parseMatrix.outputs.wpVersion }} + WP_ENV_PHP_VERSION: ${{ matrix.php }} + run: pnpm --filter=woocommerce env:test - - name: Run tests - working-directory: plugins/woocommerce - run: pnpm run test --filter=woocommerce --color + - name: Run Tests + env: + WP_ENV_CORE: ${{ steps.parseMatrix.outputs.wpVersion }} + WP_ENV_PHP_VERSION: ${{ matrix.php }} + run: pnpm --filter=woocommerce test:unit:env diff --git a/.github/workflows/release-changelog.yml b/.github/workflows/release-changelog.yml deleted file mode 100644 index ab77d208efb..00000000000 --- a/.github/workflows/release-changelog.yml +++ /dev/null @@ -1,119 +0,0 @@ -name: 'Release: Generate changelog' -on: - workflow_dispatch: - inputs: - releaseBranch: - description: 'The name of the release branch, in the format `release/x.y`' - required: true - releaseVersion: - description: 'The version of the release, in the format `x.y`' - required: true - -env: - GIT_COMMITTER_NAME: 'WooCommerce Bot' - GIT_COMMITTER_EMAIL: 'no-reply@woocommerce.com' - GIT_AUTHOR_NAME: 'WooCommerce Bot' - GIT_AUTHOR_EMAIL: 'no-reply@woocommerce.com' - -permissions: {} - -jobs: - create-changelog-prs: - runs-on: ubuntu-20.04 - permissions: - contents: write - pull-requests: write - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Setup WooCommerce Monorepo - uses: ./.github/actions/setup-woocommerce-monorepo - with: - build: false - - - name: 'Git fetch the release branch' - run: git fetch origin ${{ inputs.releaseBranch }} - - - name: 'Checkout the release branch' - run: git checkout ${{ inputs.releaseBranch }} - - - name: 'Create a new branch for the changelog update PR' - run: git checkout -b ${{ format( 'update/{0}-changelog', inputs.releaseVersion ) }} - - - name: 'Generate the changelog file' - run: pnpm --filter=woocommerce run changelog write --add-pr-num -n -vvv --use-version ${{ inputs.releaseVersion }} - - - name: Checkout pnpm-lock.yaml to prevent issues - run: git checkout pnpm-lock.yaml - - - name: 'git rm deleted files' - run: git rm $(git ls-files --deleted) - - - name: 'Commit deletion' - run: git commit -m "Delete changelog files from ${{ inputs.releaseVersion }} release" - - - name: 'Remember the deletion commit hash' - id: rev-parse - run: echo "hash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT - - - name: 'Insert NEXT_CHANGELOG contents into readme.txt' - run: php .github/workflows/scripts/release-changelog.php - - - name: 'git add readme.txt' - run: git add plugins/woocommerce/readme.txt - - - name: 'Commit readme' - run: git commit -m "Update the readme files for the ${{ inputs.releaseVersion }} release" - - - name: 'Push update branch to origin' - run: git push origin ${{ format( 'update/{0}-changelog', inputs.releaseVersion ) }} - - - name: 'Stash any other undesired changes' - run: git stash - - - name: 'Checkout trunk' - run: git checkout trunk - - - name: 'Create a branch for the changelog files deletion' - run: git checkout -b ${{ format( 'delete/{0}-changelog', inputs.releaseVersion ) }} - - - name: 'Cherry-pick the deletion commit' - run: git cherry-pick ${{ steps.rev-parse.outputs.hash }} - - - name: 'Push deletion branch to origin' - run: git push origin ${{ format( 'delete/{0}-changelog', inputs.releaseVersion ) }} - - - name: 'Create release branch PR' - id: release-pr - uses: actions/github-script@v6 - with: - script: | - const result = await github.rest.pulls.create( { - owner: "${{ github.repository_owner }}", - repo: "${{ github.event.repository.name }}", - head: "${{ format( 'update/{0}-changelog', inputs.releaseVersion ) }}", - base: "${{ inputs.releaseBranch }}", - title: "${{ format( 'Release: Prepare the changelog for {0}', inputs.releaseVersion ) }}", - body: "${{ format( 'This pull request was automatically generated during the code freeze to prepare the changelog for {0}', inputs.releaseVersion ) }}" - } ); - - return result.data.number; - - - name: 'Create trunk PR' - id: trunk-pr - uses: actions/github-script@v6 - with: - script: | - const result = await github.rest.pulls.create( { - owner: "${{ github.repository_owner }}", - repo: "${{ github.event.repository.name }}", - head: "${{ format( 'delete/{0}-changelog', inputs.releaseVersion ) }}", - base: "trunk", - title: "${{ format( 'Release: Remove {0} change files', inputs.releaseVersion ) }}", - body: "${{ format( 'This pull request was automatically generated during the code freeze to remove the changefiles from {0} that are compiled into the `{1}` branch via #{2}', inputs.releaseVersion, inputs.releaseBranch, steps.release-pr.outputs.result ) }}" - } ); - - return result.data.number; diff --git a/.github/workflows/release-code-freeze.yml b/.github/workflows/release-code-freeze.yml index 76bca3ca8d8..a67806047f4 100644 --- a/.github/workflows/release-code-freeze.yml +++ b/.github/workflows/release-code-freeze.yml @@ -41,10 +41,24 @@ jobs: with: fetch-depth: 0 + - name: Setup PNPM + uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd + with: + version: '8.3.1' + + - name: Setup Node + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c + with: + node-version-file: .nvmrc + cache: pnpm + registry-url: 'https://registry.npmjs.org' + - name: Install prerequisites run: | - npm install -g pnpm - pnpm install --filter monorepo-utils + pnpm install --filter monorepo-utils --ignore-scripts + # ignore scripts speeds up setup signficantly, but we still need to build monorepo utils + pnpm build + working-directory: tools/monorepo-utils - name: 'Check whether today is the code freeze day' id: check-freeze @@ -71,42 +85,47 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: pnpm run utils code-freeze version-bump -o ${{ github.repository_owner }} -v ${{ steps.milestone.outputs.nextDevelopmentVersion }}.0-dev + - name: Generate changelog changes + id: changelog + if: steps.check-freeze.outputs.freeze == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: pnpm run utils code-freeze changelog -o ${{ github.repository_owner }} -v ${{ steps.milestone.outputs.nextReleaseVersion }} + notify-slack: name: 'Sends code freeze notification to Slack' runs-on: ubuntu-20.04 needs: code-freeze-prep - if: ${{ inputs.skipSlackPing != true }} + if: ${{ needs.code-freeze-prep.outputs.freeze == 'true' && inputs.skipSlackPing != true }} steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Setup PNPM + uses: pnpm/action-setup@c3b53f6a16e57305370b4ae5a540c2077a1d50dd + with: + version: '8.3.1' + + - name: Setup Node + uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c + with: + node-version-file: .nvmrc + cache: pnpm + registry-url: 'https://registry.npmjs.org' + + - name: Install prerequisites + run: | + pnpm install --filter monorepo-utils --ignore-scripts + # ignore scripts speeds up setup signficantly, but we still need to build monorepo utils + pnpm build + working-directory: tools/monorepo-utils + - name: Slack - uses: archive/github-actions-slack@v2.0.0 id: notify - with: - slack-bot-user-oauth-access-token: ${{ secrets.CODE_FREEZE_BOT_TOKEN }} - slack-channel: ${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_CHANNEL }} - slack-text: | - :warning-8c: ${{ needs.code-freeze-prep.outputs.nextReleaseVersion }} Code Freeze :ice_cube: - - The automation to cut the release branch for ${{ needs.code-freeze-prep.outputs.nextReleaseVersion }} has run. Any PRs that were not already merged will be a part of ${{ needs.maybe-create-next-milestone-and-release-branch.outputs.nextDevelopmentVersion }} by default. If you have something that needs to make ${{ needs.maybe-create-next-milestone-and-release-branch.outputs.nextReleaseVersion }} that hasn't yet been merged, please see the <${{ secrets.FG_LINK }}/code-freeze-for-woocommerce-core-release/|fieldguide page for the code freeze>. - - trigger-changelog-action: - name: 'Trigger changelog action' - runs-on: ubuntu-20.04 - permissions: - actions: write - needs: code-freeze-prep - if: needs.code-freeze-prep.outputs.freeze == 'true' - steps: - - name: 'Trigger changelog action' - uses: actions/github-script@v6 - with: - script: | - github.rest.actions.createWorkflowDispatch({ - owner: context.repo.owner, - repo: context.repo.repo, - workflow_id: 'release-changelog.yml', - ref: 'trunk', - inputs: { - releaseVersion: "${{ needs.code-freeze-prep.outputs.nextReleaseVersion }}", - releaseBranch: "${{ needs.code-freeze-prep.outputs.nextReleaseBranch }}" - } - }) + run: | + pnpm utils slack "${{ secrets.CODE_FREEZE_BOT_TOKEN }}" " + :warning-8c: ${{ needs.code-freeze-prep.outputs.nextReleaseVersion }} Code Freeze :ice_cube: + The automation to cut the release branch for ${{ needs.code-freeze-prep.outputs.nextReleaseVersion }} has run. Any PRs that were not already merged will be a part of ${{ needs.code-freeze-prep.outputs.nextDevelopmentVersion }} by default. If you have something that needs to make ${{ needs.code-freeze-prep.outputs.nextReleaseVersion }} that hasn't yet been merged, please see the <${{ secrets.FG_LINK }}/code-freeze-for-woocommerce-core-release/|fieldguide page for the code freeze>. + " "${{ inputs.slackChannelOverride || secrets.WOO_RELEASE_SLACK_CHANNEL }}" diff --git a/.github/workflows/scripts/parse-wp-version.js b/.github/workflows/scripts/parse-wp-version.js new file mode 100644 index 00000000000..f3f3c420368 --- /dev/null +++ b/.github/workflows/scripts/parse-wp-version.js @@ -0,0 +1,89 @@ +const https = require( 'http' ); + +/** + For convenience, this method will convert between a display-friendly version format and one used + internally by wp-env. We lean towards using WordPress.org ZIPs which requires us to reference + the full URL to the archive. For instance, instead of needing the action to fully define the + URL to the nightly build we can pass "nightly" to this function and retrieve it. + + @param {string} wpVersion The display-friendly version. Supports ("master", "trunk", "nightly", + "latest", "X.X" for version lines, and "X.X.X" for specific versions) + @return {Promise.} The wp-env "core" property". +**/ +module.exports = async function parseWPVersion( wpVersion ) { + // Start with versions we can infer immediately. + switch ( wpVersion ) { + case 'master': + case 'trunk': { + return 'WordPress/WordPress#master'; + } + + case 'nightly': { + return 'https://wordpress.org/nightly-builds/wordpress-latest.zip'; + } + + case 'latest': { + return 'https://wordpress.org/latest.zip'; + } + } + + return new Promise( ( resolve, reject ) => { + // We're going to download the correct zip archive based on the version they're requesting. + const parsedVersion = wpVersion.match( /([0-9]+)\.([0-9]+)(?:\.([0-9]+))?/ ); + if ( ! parsedVersion ) { + throw new Error( `Invalid 'wp-version': ${ wpVersion } must be 'trunk', 'nightly', 'latest', 'X.X', or 'X.X.X'.` ); + } + + // When they've provided a specific version we can just provide that. + if ( parsedVersion[ 3 ] !== undefined ) { + let zipVersion = `${ parsedVersion[ 1 ] }.${ parsedVersion[ 2 ] }`; + // .0 versions do not have a patch. + if ( parsedVersion[ 3 ] !== '0' ) { + zipVersion += `.${ parsedVersion[ 3 ] }`; + } + + resolve( `https://wordpress.org/wordpress-${ zipVersion }.zip` ); + } + + const request = https.get( + 'http://api.wordpress.org/core/stable-check/1.0/', + ( response ) => { + // Listen for the response data. + let data = ''; + response.on('data', (chunk) => { + data += chunk; + }); + + // Once we have the entire response we can process it. + response.on('end', () => { + // Parse the response and find the latest version of every minor release. + const latestVersions = {}; + const rawVersions = JSON.parse( data ); + for ( const v in rawVersions ) { + // Parse the version so we can find the latest. + const matches = v.match( /([0-9]+)\.([0-9]+)(?:\.([0-9]+))?/ ); + const minor = `${ matches[1] }.${ matches[2] }`; + const patch = matches[ 3 ] === undefined ? 0 : parseInt( matches[ 3 ] ); + + // We will only be keeping the latest release of each minor. + if ( latestVersions[ minor ] === undefined || patch > latestVersions[ minor ] ) { + latestVersions[ minor ] = patch; + } + } + + let zipVersion = `${ parsedVersion[ 1 ] }.${ parsedVersion[ 2 ] }`; + // .0 versions do not have a patch. + if ( latestVersions[ zipVersion ] !== 0 ) { + zipVersion += `.${ latestVersions[ zipVersion ]}`; + } + + resolve( `https://wordpress.org/wordpress-${ zipVersion }.zip` ); + }); + }, + ); + + request.on( 'error', ( error ) => { + reject( error ); + } ); + } ); +} diff --git a/.github/workflows/scripts/post-request-shared.php b/.github/workflows/scripts/post-request-shared.php index 67a6794feef..8d4bc442147 100644 --- a/.github/workflows/scripts/post-request-shared.php +++ b/.github/workflows/scripts/post-request-shared.php @@ -135,7 +135,7 @@ function get_latest_version_with_release() { } /** - * Function to retreive the sha1 reference for a given branch name. + * Function to retrieve the sha1 reference for a given branch name. * * @param string $branch The name of the branch. * @return string Returns the name of the branch, or a falsey value on error. diff --git a/.github/workflows/smoke-test-daily.yml b/.github/workflows/smoke-test-daily.yml index e91f8482ed0..7e989fafea0 100644 --- a/.github/workflows/smoke-test-daily.yml +++ b/.github/workflows/smoke-test-daily.yml @@ -48,7 +48,7 @@ jobs: working-directory: plugins/woocommerce env: UPDATE_WC: nightly - run: pnpm exec playwright test --config=tests/e2e-pw/daily.playwright.config.js update-woocommerce.spec.js + run: pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js update-woocommerce.spec.js - name: Run API tests. working-directory: plugins/woocommerce @@ -200,15 +200,17 @@ jobs: - plugin: 'WooCommerce Subscriptions' repo: WC_SUBSCRIPTIONS_REPO private: true - - plugin: 'WordPress SEO' # Yoast SEO in the UI, but the slug is wordpress-seo - repo: 'Yoast/wordpress-seo' - - plugin: 'Contact Form 7' - repo: 'takayukister/contact-form-7' + - plugin: 'Gutenberg' + repo: 'WordPress/gutenberg' + - plugin: 'Gutenberg - Nightly' + repo: 'bph/gutenberg' steps: - uses: actions/checkout@v3 - name: Setup WooCommerce Monorepo uses: ./.github/actions/setup-woocommerce-monorepo + with: + build-filters: woocommerce - name: Launch wp-env e2e environment working-directory: plugins/woocommerce @@ -224,13 +226,13 @@ jobs: PLUGIN_REPOSITORY: ${{ matrix.private && secrets[matrix.repo] || matrix.repo }} PLUGIN_NAME: ${{ matrix.plugin }} GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }} - run: pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js upload-plugin.spec.js + run: pnpm test:e2e-pw upload-plugin.spec.js - name: Run the rest of E2E tests working-directory: plugins/woocommerce env: E2E_MAX_FAILURES: 15 - run: pnpm exec playwright test --config=tests/e2e-pw/playwright.config.js + run: pnpm test:e2e-pw - name: Generate E2E Test report. if: success() || failure() @@ -321,36 +323,29 @@ jobs: matrix: include: - plugin: 'WooCommerce Payments' - repo: 'automattic/woocommerce-payments' + slug: woocommerce-payments - plugin: 'WooCommerce PayPal Payments' - repo: 'woocommerce/woocommerce-paypal-payments' + slug: woocommerce-paypal-payments - plugin: 'WooCommerce Shipping & Tax' - repo: 'automattic/woocommerce-services' - - plugin: 'WordPress SEO' # Yoast SEO in the UI, but the slug is wordpress-seo - repo: 'Yoast/wordpress-seo' - - plugin: 'Contact Form 7' - repo: 'takayukister/contact-form-7' + slug: woocommerce-services + - plugin: 'WooCommerce Subscriptions' + slug: woocommerce-subscriptions + - plugin: 'Gutenberg' + slug: gutenberg + - plugin: 'Gutenberg - Nightly' + slug: gutenberg-nightly steps: - name: Download test report artifact uses: actions/download-artifact@v3 with: name: ${{ env.ARTIFACT }} - # TODO: Add step to post job summary - - - name: Get slug - id: get-slug - uses: actions/github-script@v6 - with: - result-encoding: string - script: return "${{ matrix.repo }}".split( '/' ).pop() - - name: Publish reports run: | gh workflow run publish-test-reports-daily-plugins.yml \ -f run_id=$RUN_ID \ -f artifact="${{ env.ARTIFACT }}" \ -f plugin="${{ matrix.plugin }}" \ - -f slug="${{ steps.get-slug.outputs.result }}" \ + -f slug="${{ matrix.slug }}" \ -f s3_root=public \ --repo woocommerce/woocommerce-test-reports diff --git a/.github/workflows/smoke-test-release.yml b/.github/workflows/smoke-test-release.yml index bc07de36ba7..afa0adc319a 100644 --- a/.github/workflows/smoke-test-release.yml +++ b/.github/workflows/smoke-test-release.yml @@ -14,7 +14,7 @@ permissions: {} env: E2E_WP_LATEST_ARTIFACT: E2E test on release smoke test site with WP Latest (run ${{ github.run_number }}) E2E_UPDATE_WC_ARTIFACT: WooCommerce version update test on release smoke test site (run ${{ github.run_number }}) - + SLACK_BLOCKS_ARTIFACT: slack-blocks jobs: get-tag: name: Get WooCommerce release tag @@ -122,12 +122,26 @@ jobs: -f test_type="e2e" \ --repo woocommerce/woocommerce-test-reports + - name: Create Slack block + if: | + success() || ( + failure() && steps.run-e2e-composite-action.outputs.result == 'failure' + ) + uses: ./.github/actions/tests/slack-summary-on-release/slack-blocks + with: + test-name: WC Update test + e2e-result: ${{ steps.run-e2e-composite-action.outputs.result }} + env-slug: wp-latest + release-version: ${{ needs.get-tag.outputs.tag }} + api-wp-latest: name: API on WP Latest runs-on: ubuntu-20.04 needs: [get-tag, e2e-update-wc] permissions: contents: read + outputs: + result: ${{ steps.run-api-composite-action.outputs.result }} env: ALLURE_REPORT_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/api-test-report/allure-report ALLURE_RESULTS_DIR: ${{ github.workspace }}/plugins/woocommerce/tests/api-core-tests/api-test-report/allure-results @@ -178,6 +192,18 @@ jobs: -f test_type="api" \ --repo woocommerce/woocommerce-test-reports + - name: Create Slack block + if: | + success() || ( + failure() && steps.run-api-composite-action.outputs.result == 'failure' + ) + uses: ./.github/actions/tests/slack-summary-on-release/slack-blocks + with: + test-name: WP Latest + api-result: ${{ steps.run-api-composite-action.outputs.result }} + env-slug: wp-latest + release-version: ${{ needs.get-tag.outputs.tag }} + e2e-wp-latest: name: E2E on WP Latest runs-on: ubuntu-20.04 @@ -268,6 +294,19 @@ jobs: -f test_type="e2e" \ --repo woocommerce/woocommerce-test-reports + - name: Create Slack block + if: | + success() || ( + failure() && steps.run-e2e-composite-action.outputs.result == 'failure' + ) + uses: ./.github/actions/tests/slack-summary-on-release/slack-blocks + with: + test-name: WP Latest + api-result: ${{ needs.api-wp-latest.outputs.result }} + e2e-result: ${{ steps.run-e2e-composite-action.outputs.result }} + env-slug: wp-latest + release-version: ${{ needs.get-tag.outputs.tag }} + get-wp-versions: name: Get WP L-1 & L-2 version numbers needs: [get-tag] @@ -328,6 +367,8 @@ jobs: - name: Setup WooCommerce Monorepo uses: ./.github/actions/setup-woocommerce-monorepo + with: + build-filters: woocommerce - name: Launch WP Env working-directory: plugins/woocommerce @@ -360,7 +401,7 @@ jobs: uses: ./.github/actions/tests/run-api-tests with: report-name: ${{ env.API_WP_LATEST_X_ARTIFACT }} - tests: hello + tests: hello.test.js env: ALLURE_RESULTS_DIR: ${{ env.API_ALLURE_RESULTS_DIR }} ALLURE_REPORT_DIR: ${{ env.API_ALLURE_REPORT_DIR }} @@ -435,6 +476,22 @@ jobs: -f test_type="e2e" \ --repo woocommerce/woocommerce-test-reports + - name: Create Slack block + if: | + success() || ( + failure() && ( + steps.run-api-composite-action.outputs.result == 'failure' || + steps.run-e2e-composite-action.outputs.result == 'failure' + ) + ) + uses: ./.github/actions/tests/slack-summary-on-release/slack-blocks + with: + test-name: ${{ matrix.version.description }} (${{ matrix.version.number }}) + api-result: ${{ steps.run-api-composite-action.outputs.result }} + e2e-result: ${{ steps.run-e2e-composite-action.outputs.result }} + env-slug: ${{ matrix.version.env_description }} + release-version: ${{ needs.get-wp-versions.outputs.tag }} + test-php-versions: name: Test against PHP ${{ matrix.php_version }} runs-on: ubuntu-20.04 @@ -456,6 +513,8 @@ jobs: - name: Setup WooCommerce Monorepo uses: ./.github/actions/setup-woocommerce-monorepo + with: + build-filters: woocommerce - name: Launch WP Env working-directory: plugins/woocommerce @@ -482,7 +541,7 @@ jobs: uses: ./.github/actions/tests/run-api-tests with: report-name: ${{ env.API_ARTIFACT }} - tests: hello + tests: hello.test.js env: ALLURE_RESULTS_DIR: ${{ env.API_ALLURE_RESULTS_DIR }} ALLURE_REPORT_DIR: ${{ env.API_ALLURE_REPORT_DIR }} @@ -557,6 +616,22 @@ jobs: -f test_type="e2e" \ --repo woocommerce/woocommerce-test-reports + - name: Create Slack block + if: | + success() || ( + failure() && ( + steps.run-api-composite-action.outputs.result == 'failure' || + steps.run-e2e-composite-action.outputs.result == 'failure' + ) + ) + uses: ./.github/actions/tests/slack-summary-on-release/slack-blocks + with: + test-name: PHP ${{ matrix.php_version }} + api-result: ${{ steps.run-api-composite-action.outputs.result }} + e2e-result: ${{ steps.run-e2e-composite-action.outputs.result }} + env-slug: php-${{ matrix.php_version }} + release-version: ${{ needs.get-tag.outputs.tag }} + test-plugins: name: With ${{ matrix.plugin }} runs-on: ubuntu-20.04 @@ -582,18 +657,20 @@ jobs: repo: WC_SUBSCRIPTIONS_REPO private: true env_description: 'woocommerce-subscriptions' - - plugin: 'WordPress SEO' # Yoast SEO in the UI, but the slug is wordpress-seo - repo: 'Yoast/wordpress-seo' - env_description: 'wordpress-seo' - - plugin: 'Contact Form 7' - repo: 'takayukister/contact-form-7' - env_description: 'contact-form-7' + - plugin: 'Gutenberg' + repo: 'WordPress/gutenberg' + env_description: 'gutenberg' + - plugin: 'Gutenberg - Nightly' + repo: 'bph/gutenberg' + env_description: 'gutenberg-nightly' steps: - name: Checkout uses: actions/checkout@v3 - name: Setup WooCommerce Monorepo uses: ./.github/actions/setup-woocommerce-monorepo + with: + build-filters: woocommerce - name: Launch WP Env working-directory: plugins/woocommerce @@ -656,3 +733,54 @@ jobs: -f env_description="${{ matrix.env_description }}" \ -f test_type="e2e" \ --repo woocommerce/woocommerce-test-reports + + - name: Create Slack block + if: | + success() || ( + failure() && steps.run-e2e-composite-action.outputs.result == 'failure' ) + uses: ./.github/actions/tests/slack-summary-on-release/slack-blocks + with: + test-name: With ${{ matrix.plugin }} + e2e-result: ${{ steps.run-e2e-composite-action.outputs.result }} + env-slug: ${{ matrix.env_description }} + release-version: ${{ needs.get-tag.outputs.tag }} + + post-slack-summary: + name: Post Slack summary + runs-on: ubuntu-20.04 + permissions: + contents: read + if: | + success() || ( + failure() && contains( needs.*.result, 'failure' ) + ) + needs: + - e2e-wp-latest + - get-tag + - test-php-versions + - test-plugins + - test-wp-versions + steps: + - uses: actions/checkout@v3 + + - name: Download all slack blocks + id: download-slack-blocks + uses: actions/download-artifact@v3 + with: + name: ${{ env.SLACK_BLOCKS_ARTIFACT }} + path: /tmp/slack-payload + + - name: Construct payload from all blocks + id: run-payload-action + uses: ./.github/actions/tests/slack-summary-on-release/slack-payload + with: + release-version: ${{ needs.get-tag.outputs.tag }} + blocks-dir: ${{ steps.download-slack-blocks.outputs.download-path }} + + - name: Send Slack message + uses: slackapi/slack-github-action@v1.23.0 + with: + channel-id: ${{ secrets.RELEASE_TEST_SLACK_CHANNEL }} + payload: ${{ steps.run-payload-action.outputs.payload }} + env: + SLACK_BOT_TOKEN: ${{ secrets.E2E_SLACK_TOKEN }} diff --git a/.syncpackrc b/.syncpackrc index 832c6f62e33..baf0716cbc6 100644 --- a/.syncpackrc +++ b/.syncpackrc @@ -78,7 +78,6 @@ "@wordpress/eslint-plugin", "@wordpress/babel-plugin-import-jsx-pragma", "@wordpress/babel-preset-default", - "@wordpress/env", "@wordpress/stylelint-config", "@wordpress/prettier-config", "@wordpress/scripts", @@ -116,6 +115,15 @@ ], "isIgnored": true }, + { + "dependencies": [ + "@wordpress/env" + ], + "packages": [ + "**" + ], + "pinVersion": "^7.0.0" + }, { "dependencies": [ "@wordpress/**" diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index c531e6f45a9..cb3671f4066 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -23,26 +23,26 @@ Here are some examples of the ways you can use Turborepo / pnpm commands: ```bash # Lint and build all plugins, packages, and tools. Note the use of `-r` for lint, # turbo does not run the lint at this time. -pnpm run -r lint && pnpm run build +pnpm run -r lint && pnpm run build # Build WooCommerce Core and all of its dependencies -pnpm run --filter='woocommerce' build +pnpm run --filter='woocommerce' build # Lint the @woocommerce/components package - note the different argument order, turbo scripts # are not running lints at this point in time. -pnpm run -r --filter='@woocommerce/components' lint +pnpm run -r --filter='@woocommerce/components' lint # Test all of the @woocommerce scoped packages -pnpm run --filter='@woocommerce/*' test +pnpm run --filter='@woocommerce/*' test # Build all of the JavaScript packages -pnpm run --filter='./packages/js/*' build +pnpm run --filter='./packages/js/*' build # Build everything except WooCommerce Core -pnpm run --filter='!woocommerce' build +pnpm run --filter='!woocommerce' build # Build everything that has changed since the last commit -pnpm run --filter='[HEAD^1]' build +pnpm run --filter='[HEAD^1]' build ``` ### Cache busting Turbo @@ -90,3 +90,25 @@ pnpm -- wp-env destroy Each of the [plugins in our repository](plugins) support using this tool to spin up a development environment. Note that rather than having a single top-level environment, each plugin has its own. This is done in order to prevent conflicts between them. Please check out [the official documentation](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-env/) if you would like to learn more about this tool. + +## Troubleshooting + +### Installing PHP in Unix (e.g. Ubuntu) + +Many unix systems such as Ubuntu will have PHP already installed. Sometimes without the extra packages you need to run WordPress and this will cause you to run into troubles. + +Use your package manager to add the extra PHP packages you'll need. +e.g. in Ubuntu you can run: + +``` +sudo apt update +sudo apt install php-bcmath \ + php-curl \ + php-imagick \ + php-intl \ + php-json \ + php-mbstring \ + php-mysql \ + php-xml \ + php-zip +``` diff --git a/README.md b/README.md index 3c4ba63c3ac..56778877724 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ To get up and running within the WooCommerce Monorepo, you will need to make sur - [NVM](https://github.com/nvm-sh/nvm#installing-and-updating): While you can always install Node through other means, we recommend using NVM to ensure you're aligned with the version used by our development teams. Our repository contains [an `.nvmrc` file](.nvmrc) which helps ensure you are using the correct version of Node. - [PNPM](https://pnpm.io/installation): Our repository utilizes PNPM to manage project dependencies and run various scripts involved in building and testing projects. -- [PHP 7.2+](https://www.php.net/manual/en/install.php): WooCommerce Core currently features a minimum PHP version of 7.2. It is also needed to run Composer and various project build scripts. +- [PHP 7.2+](https://www.php.net/manual/en/install.php): WooCommerce Core currently features a minimum PHP version of 7.2. It is also needed to run Composer and various project build scripts. See [troubleshooting](DEVELOPMENT.md#troubleshooting) for troubleshooting problems installing PHP. - [Composer](https://getcomposer.org/doc/00-intro.md): We use Composer to manage all of the dependencies for PHP packages and plugins. Once you've installed all of the prerequisites, you can run the following commands to get everything working. diff --git a/changelog.txt b/changelog.txt index 6005a873509..3d8ebf87944 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,131 @@ == Changelog == += 7.7.0 2023-05-10 = + +**WooCommerce** + +* Fix - Removing auto-draft as wc post type to resolve publish time bug. [#38099](https://github.com/woocommerce/woocommerce/pull/38099) +* Fix - Handle updating customer when user_registered is 0000-00-00 00:00:00. [#37907](https://github.com/woocommerce/woocommerce/pull/37907) +* Fix - Sync up date_column_name default for orders table, between stats and table data. [#37927](https://github.com/woocommerce/woocommerce/pull/37927) +* Fix - Fix regression in supporting nested date query arguments in HPOS. [#37827](https://github.com/woocommerce/woocommerce/pull/37827) +* Fix - Fix disabled "Save attributes" button [#37790](https://github.com/woocommerce/woocommerce/pull/37790) +* Fix - Accessibility update for product_categories shortcode. [#37445](https://github.com/woocommerce/woocommerce/pull/37445) +* Fix - Add sort order to migration script for consistency. [#37545](https://github.com/woocommerce/woocommerce/pull/37545) +* Fix - Add support for end_at ID to allow partial verification. [#37446](https://github.com/woocommerce/woocommerce/pull/37446) +* Fix - Avoid over-aggressive escaping of the payment gateway title in the context of the checkout thank you page. [#37481](https://github.com/woocommerce/woocommerce/pull/37481) +* Fix - Corrects imported ContainerInterface. It was not replaced because of the leading backslash. [#37334](https://github.com/woocommerce/woocommerce/pull/37334) +* Fix - Delete shipping zone count transient on woocommerce_shipping_zone_method_added and woocommerce_after_shipping_zone_object_save [#37693](https://github.com/woocommerce/woocommerce/pull/37693) +* Fix - Delete tax lookup and order stats database records when an order is deleted while orders table is authoritative [#36601](https://github.com/woocommerce/woocommerce/pull/36601) +* Fix - fixed bug where adjust_download_permissions was being scheduled on variable products without downloadable variations [#34828](https://github.com/woocommerce/woocommerce/pull/34828) +* Fix - Fixed the attributes table styling in TT2 tabs content area [#37639](https://github.com/woocommerce/woocommerce/pull/37639) +* Fix - Fix ellipsis menu overlaps on small screen [#37583](https://github.com/woocommerce/woocommerce/pull/37583) +* Fix - Fixes a failing e2e test for product variations [#37246](https://github.com/woocommerce/woocommerce/pull/37246) +* Fix - Fix global button aria-disabled style [#37461](https://github.com/woocommerce/woocommerce/pull/37461) +* Fix - Fix incorrect variable name in api-core-tests [#37388](https://github.com/woocommerce/woocommerce/pull/37388) +* Fix - Fix missing result prop in wcadmin_install_plugin_error track [#37466](https://github.com/woocommerce/woocommerce/pull/37466) +* Fix - Fix special characters not rendered in admin titles [#37546](https://github.com/woocommerce/woocommerce/pull/37546) +* Fix - Fix table alias issue in order field queries. [#37560](https://github.com/woocommerce/woocommerce/pull/37560) +* Fix - Fix the type for order items in the schema definition of REST API v1 and V2 [#35940](https://github.com/woocommerce/woocommerce/pull/35940) +* Fix - Lock the product block editor template root [#37685](https://github.com/woocommerce/woocommerce/pull/37685) +* Fix - Make migration more strict by removing IGNORE [#37595](https://github.com/woocommerce/woocommerce/pull/37595) +* Fix - Minor fixup for getting order ids in verify db command. [#37576](https://github.com/woocommerce/woocommerce/pull/37576) +* Fix - Refetch data for "Installed extensions" card after installing a recommended marketing channel. [#37300](https://github.com/woocommerce/woocommerce/pull/37300) +* Fix - Remove double checking for woocommerce_tax_display_cart filter. [#37617](https://github.com/woocommerce/woocommerce/pull/37617) +* Fix - Remove unique constraint from order_key, since orders can be created with empty order keys, which then conflict with the constraint. [#37594](https://github.com/woocommerce/woocommerce/pull/37594) +* Fix - Removing modification to rest_namespace on post type and replacing with middleware. [#37621](https://github.com/woocommerce/woocommerce/pull/37621) +* Fix - Replace information_schema queries in favor of create table parsing to remove foreign key constraints during updates [#37299](https://github.com/woocommerce/woocommerce/pull/37299) +* Fix - Restores comments (reviews) to the product editor. [#37457](https://github.com/woocommerce/woocommerce/pull/37457) +* Fix - Revert changes to use window.fetch in legacy cart JS [#37463](https://github.com/woocommerce/woocommerce/pull/37463) +* Fix - Synchronized SSR from template to REST API. [#37425](https://github.com/woocommerce/woocommerce/pull/37425) +* Fix - Updates an e2e variable name to be more descriptive [#37448](https://github.com/woocommerce/woocommerce/pull/37448) +* Fix - update select all to checkbox in menu editor [#37562](https://github.com/woocommerce/woocommerce/pull/37562) +* Fix - Use first meta value for HPOS migration when there are duplicates for flat column. [#37676](https://github.com/woocommerce/woocommerce/pull/37676) +* Fix - Hide stock status field if stock management is enabled. [#37890](https://github.com/woocommerce/woocommerce/pull/37890) +* Add - Add plugin installer version independent of WP cron. [#37753](https://github.com/woocommerce/woocommerce/pull/37753) +* Add - Add a category for product editor blocks [#37347](https://github.com/woocommerce/woocommerce/pull/37347) +* Add - Add country query param to payment gateway data sources [#37443](https://github.com/woocommerce/woocommerce/pull/37443) +* Add - Add image configuration to the product block template [#37340](https://github.com/woocommerce/woocommerce/pull/37340) +* Add - Add images block to product editor template [#37455](https://github.com/woocommerce/woocommerce/pull/37455) +* Add - add import webp support [#37307](https://github.com/woocommerce/woocommerce/pull/37307) +* Add - Adding charge sales tax field to product block editor template. [#37582](https://github.com/woocommerce/woocommerce/pull/37582) +* Add - Adding checkbox, conditional and inventory email blocks to product blocks editor. [#37646](https://github.com/woocommerce/woocommerce/pull/37646) +* Add - Adding inventory section and sku blocks to product block editor. [#37623](https://github.com/woocommerce/woocommerce/pull/37623) +* Add - Add method delete_meta_data_value to WC_Data objects [#37667](https://github.com/woocommerce/woocommerce/pull/37667) +* Add - Add methods to OrderUtil to get the names of order database tables [#37624](https://github.com/woocommerce/woocommerce/pull/37624) +* Add - Add pricing section to the pricing tab [#37513](https://github.com/woocommerce/woocommerce/pull/37513) +* Add - Add product editor blocks to assets build folder [#37318](https://github.com/woocommerce/woocommerce/pull/37318) +* Add - Add product schedule sale pricing block to blocks template definition [#37567](https://github.com/woocommerce/woocommerce/pull/37567) +* Add - Add product shipping fee block to blocks template definition [#37642](https://github.com/woocommerce/woocommerce/pull/37642) +* Add - Add summary block [#37302](https://github.com/woocommerce/woocommerce/pull/37302) +* Add - Add tax class to product editor template [#37529](https://github.com/woocommerce/woocommerce/pull/37529) +* Add - Add tracks events to attributes tab [#37622](https://github.com/woocommerce/woocommerce/pull/37622) +* Add - Add tracks events to variations tab [#37607](https://github.com/woocommerce/woocommerce/pull/37607) +* Add - Add Woo Payments feature slotfill on homepage [#37768](https://github.com/woocommerce/woocommerce/pull/37768) +* Add - Allows the WP, WC & PHP version to be specified in .wp-env.json for e2e and api tests [#37587](https://github.com/woocommerce/woocommerce/pull/37587) +* Add - Change variations dropdown menu visibility [#37558](https://github.com/woocommerce/woocommerce/pull/37558) +* Add - Register product track inventory block [#37585](https://github.com/woocommerce/woocommerce/pull/37585) +* Add - Register woocommerce/product-shipping-dimensions-fields block [#37683](https://github.com/woocommerce/woocommerce/pull/37683) +* Update - Update WooCommerce Blocks to 10.0.2 [#37818](https://github.com/woocommerce/woocommerce/pull/37818) +* Update - Update WC Blocks to include changes from 9.9.0, 10.0.0 and 10.0.1 [#37662](https://github.com/woocommerce/woocommerce/pull/37662) +* Update - Add default priority for countries that are not in the payment recommendation map [#37590](https://github.com/woocommerce/woocommerce/pull/37590) +* Update - Add Payoneer, zipco payment gateways and update Klarna available countries [#37329](https://github.com/woocommerce/woocommerce/pull/37329) +* Update - Filter out marketing channels in "Installed extensions" and "Discover more marketing tools" cards. [#37126](https://github.com/woocommerce/woocommerce/pull/37126) +* Update - FlexSlider always uses Web Animations API for "slide" animations. [#36987](https://github.com/woocommerce/woocommerce/pull/36987) +* Update - Make Multichannel Marketing the default new UI for Marketing page; remove classic Marketing page and unused code. [#37430](https://github.com/woocommerce/woocommerce/pull/37430) +* Update - Migrate steps location task to TS [#37257](https://github.com/woocommerce/woocommerce/pull/37257) +* Update - Refactoring product editor more menu items, and using in block editor slot fills. [#37255](https://github.com/woocommerce/woocommerce/pull/37255) +* Update - Register product editor blocks server-side [#37339](https://github.com/woocommerce/woocommerce/pull/37339) +* Update - Remove multichannel marketing info from WC Tracker [#37438](https://github.com/woocommerce/woocommerce/pull/37438) +* Update - Remove theme step from onboarding wizard [#37671](https://github.com/woocommerce/woocommerce/pull/37671) +* Update - Replacing multiple components on the block product page with a single hook. [#37283](https://github.com/woocommerce/woocommerce/pull/37283) +* Update - Show different error message when deleting an attribute used in variations [#37527](https://github.com/woocommerce/woocommerce/pull/37527) +* Update - Show tooltip in Save attributes button instead of using title attribute [#37345](https://github.com/woocommerce/woocommerce/pull/37345) +* Update - Support min_php_version and min_wp_version for the free extensions feed [#37694](https://github.com/woocommerce/woocommerce/pull/37694) +* Update - Update payment gateway recommendation priority [#37442](https://github.com/woocommerce/woocommerce/pull/37442) +* Update - Update textdomain in woocommerce-blocks *.json files to `woocommerce` [#37234](https://github.com/woocommerce/woocommerce/pull/37234) +* Dev - Fix recent failures in "Smoke test release" workflow. [#37783](https://github.com/woocommerce/woocommerce/pull/37783) +* Dev - Add composer scripts for linting with phpcs-changed [#37465](https://github.com/woocommerce/woocommerce/pull/37465) +* Dev - Add tracks event to gather onboarding heuristics [#37767](https://github.com/woocommerce/woocommerce/pull/37767) +* Dev - Bump required PHP version to 7.3 and PHPUnit version to 9 [#37366](https://github.com/woocommerce/woocommerce/pull/37366) +* Dev - Code refactor on marketing components. [#37444](https://github.com/woocommerce/woocommerce/pull/37444) +* Dev - Dev - Add customer object parameter to taxable address filter [#37426](https://github.com/woocommerce/woocommerce/pull/37426) +* Dev - Dev - Allow to filter wc_help_tip [#37485](https://github.com/woocommerce/woocommerce/pull/37485) +* Dev - Fix WP latest-2 version retrieval in the "Smoke test release" workflow. [#37675](https://github.com/woocommerce/woocommerce/pull/37675) +* Dev - Item controls for attribute creation are always visible [#37620](https://github.com/woocommerce/woocommerce/pull/37620) +* Dev - Migrate woocommerce-payments task to TS and remove connect.js from task fills [#37308](https://github.com/woocommerce/woocommerce/pull/37308) +* Dev - Move additional CES-related components to @woocommerce/customer-effort-score. [#37316](https://github.com/woocommerce/woocommerce/pull/37316) +* Dev - Move components to @woocommerce/product-editor [#37131](https://github.com/woocommerce/woocommerce/pull/37131) +* Dev - New empty state for variations - no variations have been created yet [#37411](https://github.com/woocommerce/woocommerce/pull/37411) +* Dev - Reduce flakiness on E2E test setup. [#37410](https://github.com/woocommerce/woocommerce/pull/37410) +* Dev - Rename the default placeholder in the new attribute form header to New attribute [#37645](https://github.com/woocommerce/woocommerce/pull/37645) +* Dev - Replaced `example.org` in tests with WP_TESTS_DOMAIN for consistency with WordPress Core. [#37742](https://github.com/woocommerce/woocommerce/pull/37742) +* Dev - Reset variable product tour after running e2e tests. [#37680](https://github.com/woocommerce/woocommerce/pull/37680) +* Dev - Run E2E tests on PR merge to trunk. [#37033](https://github.com/woocommerce/woocommerce/pull/37033) +* Dev - Set quantity value when stock tracking is enabled [#37304](https://github.com/woocommerce/woocommerce/pull/37304) +* Dev - Simplify boolean expression before && in Marketing page. [#37452](https://github.com/woocommerce/woocommerce/pull/37452) +* Dev - Smoke test WooCommerce with plugins installed on releases. [#37361](https://github.com/woocommerce/woocommerce/pull/37361) +* Dev - Split can create product, attributes and variations, edit variations and delete variations into smaller tests to avoid timing out [#37733](https://github.com/woocommerce/woocommerce/pull/37733) +* Dev - Update webpack config to use @woocommerce/internal-style-build's parser config [#37195](https://github.com/woocommerce/woocommerce/pull/37195) +* Tweak - Update plugin listing description [#38074](https://github.com/woocommerce/woocommerce/pull/38074) +* Tweak - Changed label for button to add a new global attribute value from the product screen. [#37414](https://github.com/woocommerce/woocommerce/pull/37414) +* Tweak - Default to sorting orders by date (desc) when HPOS is active. [#37565](https://github.com/woocommerce/woocommerce/pull/37565) +* Tweak - Exclude empty attributes from the attribute count when tracking product updates. [#37718](https://github.com/woocommerce/woocommerce/pull/37718) +* Tweak - Fix typo in a function comment. [#37746](https://github.com/woocommerce/woocommerce/pull/37746) +* Tweak - Fix typo in Stats controller [#37407](https://github.com/woocommerce/woocommerce/pull/37407) +* Tweak - Fix typos in comments in REST API customers controller [#37405](https://github.com/woocommerce/woocommerce/pull/37405) +* Tweak - Remove the multichannel marketing feature flag from database since it's the default option now. [#37454](https://github.com/woocommerce/woocommerce/pull/37454) +* Tweak - Remove timeouts in e2e tests for variable products and analytics. [#37335](https://github.com/woocommerce/woocommerce/pull/37335) +* Tweak - Update mobile app image resolution [#37506](https://github.com/woocommerce/woocommerce/pull/37506) +* Tweak - Update style of product attributes tab empty state. [#37429](https://github.com/woocommerce/woocommerce/pull/37429) +* Tweak - Update to the merchant variable product e2e test [#37714](https://github.com/woocommerce/woocommerce/pull/37714) +* Performance - Improve search count query performance by avoiding LEFT JOIN in favor of subquery. [#36688](https://github.com/woocommerce/woocommerce/pull/36688) +* Enhancement - Added a button to download SSR to a file. [#38110](https://github.com/woocommerce/woocommerce/pull/38110) +* Enhancement - Added a woocommerce_disable_api_access_log filter to disable last access logging for rest api. [#37332](https://github.com/woocommerce/woocommerce/pull/37332) +* Enhancement - Fix rounding difference on refunds with per-line taxes [#34641](https://github.com/woocommerce/woocommerce/pull/34641) +* Enhancement - Show info message when on variations tab and no attributes have been assigned to product. [#37352](https://github.com/woocommerce/woocommerce/pull/37352) +* Enhancement - Show tour when product type is changed to variable. [#37413](https://github.com/woocommerce/woocommerce/pull/37413) + + = 7.6.1 2023-04-26 = **WooCommerce** @@ -54,7 +180,7 @@ * Fix - Prevent possible warning arising from use of woocommerce_wp_* family of functions. [#37026](https://github.com/woocommerce/woocommerce/pull/37026) * Fix - Record values for toggled checkboxes/features in settings [#37242](https://github.com/woocommerce/woocommerce/pull/37242) * Fix - Restore the sort order when orders are cached. [#36650](https://github.com/woocommerce/woocommerce/pull/36650) -* Fix - Treat order as seperate resource when validating for webhook since it's not necessarily a CPT anymore. [#36650](https://github.com/woocommerce/woocommerce/pull/36650) +* Fix - Treat order as separate resource when validating for webhook since it's not necessarily a CPT anymore. [#36650](https://github.com/woocommerce/woocommerce/pull/36650) * Fix - Update Customers report with latest user data after editing user. [#37237](https://github.com/woocommerce/woocommerce/pull/37237) * Add - Add "Create a new campaign" modal in Campaigns card in Multichannel Marketing page. [#37044](https://github.com/woocommerce/woocommerce/pull/37044) * Add - Add a cache for orders, to use when custom order tables are enabled [#35014](https://github.com/woocommerce/woocommerce/pull/35014) @@ -432,7 +558,7 @@ * Tweak - Resolve an error in the product tracking code by testing to see if the `post_type` query var is set before checking its value. [#34501](https://github.com/woocommerce/woocommerce/pull/34501) * Tweak - Simplify wording within the customer emails for on-hold orders. [#31886](https://github.com/woocommerce/woocommerce/pull/31886) * Tweak - WooCommerce has now been tested up to WordPress 6.1.x. [#35985](https://github.com/woocommerce/woocommerce/pull/35985) -* Performance - Split CALC_FOUND_ROW query into seperate count query for better performance. [#35468](https://github.com/woocommerce/woocommerce/pull/35468) +* Performance - Split CALC_FOUND_ROW query into separate count query for better performance. [#35468](https://github.com/woocommerce/woocommerce/pull/35468) * Enhancement - Add a bottom padding to the whole form [#35721](https://github.com/woocommerce/woocommerce/pull/35721) * Enhancement - Add a confirmation modal when the user tries to navigate away with unsaved changes [#35625](https://github.com/woocommerce/woocommerce/pull/35625) * Enhancement - Add a default placeholder title for newly added attributes and always show remove button for attributes [#35904](https://github.com/woocommerce/woocommerce/pull/35904) @@ -3371,7 +3497,7 @@ * Fix - Add protection around func_get_args_call for backwards compatibility. #28677 * Fix - Restore stock_status in REST API V3 response. #28731 * Fix - Revert some of the changes related to perf enhancements (27735) as it was breaking stock_status filter. #28745 -* Dev - Hook for intializing price slider in frontend. #28014 +* Dev - Hook for initializing price slider in frontend. #28014 * Dev - Add support for running a custom initialization script for tests. #28041 * Dev - Use the coenjacobs/mozart package to renamespace vendor packages. #28147 * Dev - Documentation for `wc_get_container`. #28269 @@ -5290,7 +5416,7 @@ * Fix - Fix edge case where `get_plugins` would not have the custom WooCommerce plugin headers if `get_plugins` was called early. #21669 * Fix - Prevent PHP warning when deprecated user meta starts with uppercase. #21943 * Fix - Fixed support for multiple query parameters translated to meta queries via REST API requests. #22108 -* Fix - Prevent PHP errors when trying to access non-existant report tabs. #22183 +* Fix - Prevent PHP errors when trying to access non-existent report tabs. #22183 * Fix - Filter by attributes dropdown placeholder text should not be wrapped in quotes. #22185 * Fix - Apply sale price until end of closing sale date. #22189 * Fix - Allow empty schema again when registering a custom field for the API. #22204 @@ -6612,7 +6738,7 @@ * Removed internal scroll from log viewer. * Add reply-to to admin emails. * Improved the zone setup flow. -* Made wc_get_wildcard_postcodes return the orignal postcode plus * since wildcards should match empty strings too. +* Made wc_get_wildcard_postcodes return the original postcode plus * since wildcards should match empty strings too. * Use all paid statuses in $customer->get_total_spent(). * Move location of billing email field to work with password managers. * Option to restrict selling locations by country. @@ -8122,7 +8248,7 @@ * Tweak - Flat rate shipping support for percentage factor of additional costs. * Tweak - local delivery _ pattern matching for postcodes. e.g. NG1___ would match NG1 1AA but not NG10 1AA. * Tweak - Improved layered nav OR count logic -* Tweak - Make shipping methods check if taxable, so when customer is VAT excempt taxes are not included in price. +* Tweak - Make shipping methods check if taxable, so when customer is VAT exempt taxes are not included in price. * Tweak - Coupon in admin bar new menu #3974 * Tweak - Shortcode tag filters + updated menu names to make white labelling easier. * Tweak - Removed placeholder polyfill. Use this plugin to replace functionality if required: https://wordpress.org/plugins/html5-placeholder-polyfill/ diff --git a/package.json b/package.json index 9c8ac22039e..dc0cc55d9b0 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "create-extension": "node ./tools/create-extension/index.js", "cherry-pick": "node ./tools/cherry-pick/bin/run", "sync-dependencies": "pnpm exec syncpack -- fix-mismatches", - "utils": "./tools/monorepo-utils/bin/run" + "utils": "node ./tools/monorepo-utils/dist/index.js" }, "devDependencies": { "@babel/preset-env": "^7.20.2", diff --git a/packages/js/components/changelog/add-38248 b/packages/js/components/changelog/add-38248 new file mode 100644 index 00000000000..76d51203088 --- /dev/null +++ b/packages/js/components/changelog/add-38248 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Make DateTimePickerControl a ForwardedRef component" diff --git a/packages/js/components/changelog/add-core-profiler-user-profile b/packages/js/components/changelog/add-core-profiler-user-profile new file mode 100644 index 00000000000..ab57ba63f55 --- /dev/null +++ b/packages/js/components/changelog/add-core-profiler-user-profile @@ -0,0 +1,4 @@ +Significance: patch +Type: add + +Add onKeyDown and readOnlyWhenClosed options to experimentalSelectControl diff --git a/packages/js/components/changelog/add-selecttree-singlemode b/packages/js/components/changelog/add-selecttree-singlemode new file mode 100644 index 00000000000..a35c19a4a83 --- /dev/null +++ b/packages/js/components/changelog/add-selecttree-singlemode @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add single selection mode to SelectTree diff --git a/packages/js/components/changelog/dev-migrate-select-control-to-ts b/packages/js/components/changelog/dev-migrate-select-control-to-ts new file mode 100644 index 00000000000..475884bfaa6 --- /dev/null +++ b/packages/js/components/changelog/dev-migrate-select-control-to-ts @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Migrate select control component to TS diff --git a/packages/js/components/changelog/fix-37502 b/packages/js/components/changelog/fix-37502 new file mode 100644 index 00000000000..115429c4b01 --- /dev/null +++ b/packages/js/components/changelog/fix-37502 @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak + +Correct spelling errors diff --git a/packages/js/components/changelog/fix-38210 b/packages/js/components/changelog/fix-38210 new file mode 100644 index 00000000000..26520bd3a91 --- /dev/null +++ b/packages/js/components/changelog/fix-38210 @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Wrap selected items in experimental select control diff --git a/packages/js/components/changelog/improve-selecttree-label b/packages/js/components/changelog/improve-selecttree-label new file mode 100644 index 00000000000..5a831787bf3 --- /dev/null +++ b/packages/js/components/changelog/improve-selecttree-label @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Use BaseControl in the SelectTree label diff --git a/packages/js/components/changelog/update-37727 b/packages/js/components/changelog/update-37727 new file mode 100644 index 00000000000..9b6f5259944 --- /dev/null +++ b/packages/js/components/changelog/update-37727 @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Show comma separated list in ready only mode of select tree control diff --git a/packages/js/components/changelog/update-37955_disable_drag_and_drop_images_block b/packages/js/components/changelog/update-37955_disable_drag_and_drop_images_block new file mode 100644 index 00000000000..436e516160c --- /dev/null +++ b/packages/js/components/changelog/update-37955_disable_drag_and_drop_images_block @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add allowDragging option to ImageGallery to support disabling drag and drop of images. diff --git a/packages/js/components/changelog/update-tree-item b/packages/js/components/changelog/update-tree-item new file mode 100644 index 00000000000..9473f708688 --- /dev/null +++ b/packages/js/components/changelog/update-tree-item @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +TreeControl: Fix a bug where items with children were not selected in single mode and fix a bug where navigation between items with tab was not working properly. diff --git a/packages/js/components/src/chart/README.md b/packages/js/components/src/chart/README.md index 28082ca574a..a8a4474a6cb 100644 --- a/packages/js/components/src/chart/README.md +++ b/packages/js/components/src/chart/README.md @@ -94,7 +94,7 @@ Name | Type | Default | Description `legendPosition` | One of: 'bottom', 'side', 'top', 'hidden' | `null` | Position the legend must be displayed in. If it's not defined, it's calculated depending on the viewport width and the mode `legendTotals` | Object | `null` | Values to overwrite the legend totals. If not defined, the sum of all line values will be used `screenReaderFormat` | One of type: string, func | `'%B %-d, %Y'` | A datetime formatting string or overriding function to format the screen reader labels -`showHeaderControls` | Boolean | `true` | Wether header UI controls must be displayed +`showHeaderControls` | Boolean | `true` | Whether header UI controls must be displayed `title` | String | `null` | A title describing this chart `tooltipLabelFormat` | One of type: string, func | `'%B %-d, %Y'` | A datetime formatting string or overriding function to format the tooltip label `tooltipValueFormat` | One of type: string, func | `','` | A number formatting string or function to format the value displayed in the tooltips diff --git a/packages/js/components/src/chart/index.js b/packages/js/components/src/chart/index.js index c5afe925f08..101753f08bd 100644 --- a/packages/js/components/src/chart/index.js +++ b/packages/js/components/src/chart/index.js @@ -577,7 +577,7 @@ Chart.propTypes = { PropTypes.func, ] ), /** - * Wether header UI controls must be displayed. + * Whether header UI controls must be displayed. */ showHeaderControls: PropTypes.bool, /** diff --git a/packages/js/components/src/date-time-picker-control/date-time-picker-control.tsx b/packages/js/components/src/date-time-picker-control/date-time-picker-control.tsx index 0f7d8eee0fd..edeee89611f 100644 --- a/packages/js/components/src/date-time-picker-control/date-time-picker-control.tsx +++ b/packages/js/components/src/date-time-picker-control/date-time-picker-control.tsx @@ -1,6 +1,7 @@ /** * External dependencies */ +import { Ref } from 'react'; import { format as formatDate } from '@wordpress/date'; import { createElement, @@ -9,6 +10,7 @@ import { useEffect, useMemo, useRef, + forwardRef, } from '@wordpress/element'; import { Icon, calendar } from '@wordpress/icons'; import moment, { Moment } from 'moment'; @@ -48,306 +50,329 @@ export type DateTimePickerControlProps = { placeholder?: string; help?: string | null; onChangeDebounceWait?: number; -} & Omit< React.HTMLAttributes< HTMLDivElement >, 'onChange' >; +} & Omit< React.HTMLAttributes< HTMLInputElement >, 'onChange' >; -export const DateTimePickerControl: React.FC< DateTimePickerControlProps > = ( { - currentDate, - isDateOnlyPicker = false, - is12HourPicker = true, - timeForDateOnly = 'start-of-day', - dateTimeFormat, - disabled = false, - onChange, - onBlur, - label, - placeholder, - help, - className = '', - onChangeDebounceWait = 500, -}: DateTimePickerControlProps ) => { - const instanceId = useInstanceId( DateTimePickerControl ); - const id = `inspector-date-time-picker-control-${ instanceId }`; - const inputControl = useRef< InputControl >(); +export const DateTimePickerControl = forwardRef( + function ForwardedDateTimePickerControl( + { + currentDate, + isDateOnlyPicker = false, + is12HourPicker = true, + timeForDateOnly = 'start-of-day', + dateTimeFormat, + disabled = false, + onChange, + onBlur, + label, + placeholder, + help, + className = '', + onChangeDebounceWait = 500, + ...props + }: DateTimePickerControlProps, + ref: Ref< HTMLInputElement > + ) { + const id = useInstanceId( + DateTimePickerControl, + 'inspector-date-time-picker-control', + props.id + ) as string; + const inputControl = useRef< InputControl >(); - const displayFormat = useMemo( () => { - if ( dateTimeFormat ) { - return dateTimeFormat; - } - - if ( isDateOnlyPicker ) { - return defaultDateFormat; - } - - if ( is12HourPicker ) { - return default12HourDateTimeFormat; - } - - return default24HourDateTimeFormat; - }, [ dateTimeFormat, isDateOnlyPicker, is12HourPicker ] ); - - function parseAsISODateTime( - dateString?: string | null, - assumeLocalTime = false - ): Moment { - return assumeLocalTime - ? moment( dateString, moment.ISO_8601, true ).utc() - : moment.utc( dateString, moment.ISO_8601, true ); - } - - function parseAsLocalDateTime( dateString: string | null ): Moment { - // parse input date string as local time; - // be lenient of user input and try to match any format Moment can - return moment( dateString ); - } - - const maybeForceTime = useCallback( - ( momentDate: Moment ) => { - if ( ! isDateOnlyPicker || ! momentDate.isValid() ) - return momentDate; - - // We want to set to the start/end of the local time, so - // we need to put our Moment instance into "local" mode - const updatedMomentDate = momentDate.clone().local(); - - if ( timeForDateOnly === 'start-of-day' ) { - updatedMomentDate.startOf( 'day' ); - } else if ( timeForDateOnly === 'end-of-day' ) { - updatedMomentDate.endOf( 'day' ); + const displayFormat = useMemo( () => { + if ( dateTimeFormat ) { + return dateTimeFormat; } - return updatedMomentDate; - }, - [ isDateOnlyPicker, timeForDateOnly ] - ); + if ( isDateOnlyPicker ) { + return defaultDateFormat; + } - function hasFocusLeftInputAndDropdownContent( - event: React.FocusEvent< HTMLInputElement > - ): boolean { - return ! event.relatedTarget?.closest( - '.components-dropdown__content' + if ( is12HourPicker ) { + return default12HourDateTimeFormat; + } + + return default24HourDateTimeFormat; + }, [ dateTimeFormat, isDateOnlyPicker, is12HourPicker ] ); + + function parseAsISODateTime( + dateString?: string | null, + assumeLocalTime = false + ): Moment { + return assumeLocalTime + ? moment( dateString, moment.ISO_8601, true ).utc() + : moment.utc( dateString, moment.ISO_8601, true ); + } + + function parseAsLocalDateTime( dateString: string | null ): Moment { + // parse input date string as local time; + // be lenient of user input and try to match any format Moment can + return moment( dateString ); + } + + const maybeForceTime = useCallback( + ( momentDate: Moment ) => { + if ( ! isDateOnlyPicker || ! momentDate.isValid() ) + return momentDate; + + // We want to set to the start/end of the local time, so + // we need to put our Moment instance into "local" mode + const updatedMomentDate = momentDate.clone().local(); + + if ( timeForDateOnly === 'start-of-day' ) { + updatedMomentDate.startOf( 'day' ); + } else if ( timeForDateOnly === 'end-of-day' ) { + updatedMomentDate.endOf( 'day' ); + } + + return updatedMomentDate; + }, + [ isDateOnlyPicker, timeForDateOnly ] + ); + + function hasFocusLeftInputAndDropdownContent( + event: React.FocusEvent< HTMLInputElement > + ): boolean { + return ! event.relatedTarget?.closest( + '.components-dropdown__content' + ); + } + + const formatDateTimeForDisplay = useCallback( + ( dateTime: Moment ) => { + return dateTime.isValid() + ? // @ts-expect-error TODO - fix this type error with moment + formatDate( displayFormat, dateTime.local() ) + : dateTime.creationData().input?.toString() || ''; + }, + [ displayFormat ] + ); + + function formatDateTimeAsISO( dateTime: Moment ): string { + return dateTime.isValid() + ? dateTime.utc().toISOString() + : dateTime.creationData().input?.toString() || ''; + } + + const currentDateTime = parseAsISODateTime( currentDate ); + + const [ inputString, setInputString ] = useState( + currentDateTime.isValid() + ? formatDateTimeForDisplay( maybeForceTime( currentDateTime ) ) + : '' + ); + + const inputStringDateTime = useMemo( () => { + return maybeForceTime( parseAsLocalDateTime( inputString ) ); + }, [ inputString, maybeForceTime ] ); + + // We keep a ref to the onChange prop so that we can be sure we are + // always using the more up-to-date value, even if it changes + // it while a debounced onChange handler is in progress + const onChangeRef = useRef< + DateTimePickerControlOnChangeHandler | undefined + >(); + useEffect( () => { + onChangeRef.current = onChange; + }, [ onChange ] ); + + const setInputStringAndMaybeCallOnChange = useCallback( + ( newInputString: string, isUserTypedInput: boolean ) => { + // InputControl doesn't fire an onChange if what the user has typed + // matches the current value of the input field. To get around this, + // we pull the value directly out of the input field. This fixes + // the issue where the user ends up typing the same value. Unless they + // are typing extra slow. Without this workaround, we miss the last + // character typed. + const lastTypedValue = inputControl.current.value; + + const newDateTime = maybeForceTime( + isUserTypedInput + ? parseAsLocalDateTime( lastTypedValue ) + : parseAsISODateTime( newInputString, true ) + ); + const isDateTimeSame = + newDateTime.isSame( inputStringDateTime ); + + if ( isUserTypedInput ) { + setInputString( lastTypedValue ); + } else if ( ! isDateTimeSame ) { + setInputString( formatDateTimeForDisplay( newDateTime ) ); + } + + if ( + typeof onChangeRef.current === 'function' && + ! isDateTimeSame + ) { + onChangeRef.current( + newDateTime.isValid() + ? formatDateTimeAsISO( newDateTime ) + : lastTypedValue, + newDateTime.isValid() + ); + } + }, + [ formatDateTimeForDisplay, inputStringDateTime, maybeForceTime ] + ); + + const debouncedSetInputStringAndMaybeCallOnChange = useDebounce( + setInputStringAndMaybeCallOnChange, + onChangeDebounceWait + ); + + function focusInputControl() { + if ( inputControl.current ) { + inputControl.current.focus(); + } + } + + const getUserInputOrUpdatedCurrentDate = useCallback( () => { + if ( currentDate !== undefined ) { + const newDateTime = maybeForceTime( + parseAsISODateTime( currentDate, false ) + ); + + if ( ! newDateTime.isValid() ) { + // keep the invalid string, so the user can correct it + return currentDate; + } + + if ( ! newDateTime.isSame( inputStringDateTime ) ) { + return formatDateTimeForDisplay( newDateTime ); + } + + // the new currentDate is the same date as the inputString, + // so keep exactly what the user typed in + return inputString; + } + + // the component is uncontrolled (not using currentDate), + // so just return the input string + return inputString; + }, [ + currentDate, + formatDateTimeForDisplay, + inputString, + maybeForceTime, + ] ); + + // We keep a ref to the onBlur prop so that we can be sure we are + // always using the more up-to-date value, otherwise, we get in + // any infinite loop when calling onBlur + const onBlurRef = useRef< () => void >(); + useEffect( () => { + onBlurRef.current = onBlur; + }, [ onBlur ] ); + + const callOnBlurIfDropdownIsNotOpening = useCallback( ( willOpen ) => { + if ( ! willOpen && typeof onBlurRef.current === 'function' ) { + // in case the component is blurred before a debounced + // change has been processed, immediately set the input string + // to the current value of the input field, so that + // it won't be set back to the pre-change value + setInputStringAndMaybeCallOnChange( + inputControl.current.value, + true + ); + onBlurRef.current(); + } + }, [] ); + + return ( + ( + + { + inputControl.current = element; + if ( typeof ref === 'function' ) { + ref( element ); + } + } } + disabled={ disabled } + value={ getUserInputOrUpdatedCurrentDate() } + onChange={ ( newValue: string ) => + debouncedSetInputStringAndMaybeCallOnChange( + newValue, + true + ) + } + onBlur={ ( + event: React.FocusEvent< HTMLInputElement > + ) => { + if ( + hasFocusLeftInputAndDropdownContent( event ) + ) { + // close the dropdown, which will also trigger + // the component's onBlur to be called + onClose(); + } + } } + suffix={ + + } + placeholder={ placeholder } + describedBy={ sprintf( + /* translators: A datetime format */ + __( + 'Date input describing a selected date in format %s', + 'woocommerce' + ), + dateTimeFormat + ) } + onFocus={ () => { + if ( isOpen ) { + return; // the dropdown is already open, do we don't need to do anything + } + + onToggle(); // show the dropdown + } } + aria-expanded={ isOpen } + /> + + ) } + popoverProps={ { + className: 'woocommerce-date-time-picker-control__popover', + } } + renderContent={ () => { + const Picker = isDateOnlyPicker + ? DatePicker + : WpDateTimePicker; + + return ( + + setInputStringAndMaybeCallOnChange( + newDateTimeISOString, + false + ) + } + is12Hour={ is12HourPicker } + /> + ); + } } + /> ); } - - const formatDateTimeForDisplay = useCallback( - ( dateTime: Moment ) => { - return dateTime.isValid() - ? // @ts-expect-error TODO - fix this type error with moment - formatDate( displayFormat, dateTime.local() ) - : dateTime.creationData().input?.toString() || ''; - }, - [ displayFormat ] - ); - - function formatDateTimeAsISO( dateTime: Moment ): string { - return dateTime.isValid() - ? dateTime.utc().toISOString() - : dateTime.creationData().input?.toString() || ''; - } - - const currentDateTime = parseAsISODateTime( currentDate ); - - const [ inputString, setInputString ] = useState( - currentDateTime.isValid() - ? formatDateTimeForDisplay( maybeForceTime( currentDateTime ) ) - : '' - ); - - const inputStringDateTime = useMemo( () => { - return maybeForceTime( parseAsLocalDateTime( inputString ) ); - }, [ inputString, maybeForceTime ] ); - - // We keep a ref to the onChange prop so that we can be sure we are - // always using the more up-to-date value, even if it changes - // it while a debounced onChange handler is in progress - const onChangeRef = useRef< - DateTimePickerControlOnChangeHandler | undefined - >(); - useEffect( () => { - onChangeRef.current = onChange; - }, [ onChange ] ); - - const setInputStringAndMaybeCallOnChange = useCallback( - ( newInputString: string, isUserTypedInput: boolean ) => { - // InputControl doesn't fire an onChange if what the user has typed - // matches the current value of the input field. To get around this, - // we pull the value directly out of the input field. This fixes - // the issue where the user ends up typing the same value. Unless they - // are typing extra slow. Without this workaround, we miss the last - // character typed. - const lastTypedValue = inputControl.current.value; - - const newDateTime = maybeForceTime( - isUserTypedInput - ? parseAsLocalDateTime( lastTypedValue ) - : parseAsISODateTime( newInputString, true ) - ); - const isDateTimeSame = newDateTime.isSame( inputStringDateTime ); - - if ( isUserTypedInput ) { - setInputString( lastTypedValue ); - } else if ( ! isDateTimeSame ) { - setInputString( formatDateTimeForDisplay( newDateTime ) ); - } - - if ( - typeof onChangeRef.current === 'function' && - ! isDateTimeSame - ) { - onChangeRef.current( - newDateTime.isValid() - ? formatDateTimeAsISO( newDateTime ) - : lastTypedValue, - newDateTime.isValid() - ); - } - }, - [ formatDateTimeForDisplay, inputStringDateTime, maybeForceTime ] - ); - - const debouncedSetInputStringAndMaybeCallOnChange = useDebounce( - setInputStringAndMaybeCallOnChange, - onChangeDebounceWait - ); - - function focusInputControl() { - if ( inputControl.current ) { - inputControl.current.focus(); - } - } - - const getUserInputOrUpdatedCurrentDate = useCallback( () => { - if ( currentDate !== undefined ) { - const newDateTime = maybeForceTime( - parseAsISODateTime( currentDate, false ) - ); - - if ( ! newDateTime.isValid() ) { - // keep the invalid string, so the user can correct it - return currentDate; - } - - if ( ! newDateTime.isSame( inputStringDateTime ) ) { - return formatDateTimeForDisplay( newDateTime ); - } - - // the new currentDate is the same date as the inputString, - // so keep exactly what the user typed in - return inputString; - } - - // the component is uncontrolled (not using currentDate), - // so just return the input string - return inputString; - }, [ currentDate, formatDateTimeForDisplay, inputString, maybeForceTime ] ); - - // We keep a ref to the onBlur prop so that we can be sure we are - // always using the more up-to-date value, otherwise, we get in - // any infinite loop when calling onBlur - const onBlurRef = useRef< () => void >(); - useEffect( () => { - onBlurRef.current = onBlur; - }, [ onBlur ] ); - - const callOnBlurIfDropdownIsNotOpening = useCallback( ( willOpen ) => { - if ( ! willOpen && typeof onBlurRef.current === 'function' ) { - // in case the component is blurred before a debounced - // change has been processed, immediately set the input string - // to the current value of the input field, so that - // it won't be set back to the pre-change value - setInputStringAndMaybeCallOnChange( - inputControl.current.value, - true - ); - onBlurRef.current(); - } - }, [] ); - - return ( - ( - - - debouncedSetInputStringAndMaybeCallOnChange( - newValue, - true - ) - } - onBlur={ ( - event: React.FocusEvent< HTMLInputElement > - ) => { - if ( - hasFocusLeftInputAndDropdownContent( event ) - ) { - // close the dropdown, which will also trigger - // the component's onBlur to be called - onClose(); - } - } } - suffix={ - - } - placeholder={ placeholder } - describedBy={ sprintf( - /* translators: A datetime format */ - __( - 'Date input describing a selected date in format %s', - 'woocommerce' - ), - dateTimeFormat - ) } - onFocus={ () => { - if ( isOpen ) { - return; // the dropdown is already open, do we don't need to do anything - } - - onToggle(); // show the dropdown - } } - aria-expanded={ isOpen } - /> - - ) } - popoverProps={ { - className: 'woocommerce-date-time-picker-control__popover', - } } - renderContent={ () => { - const Picker = isDateOnlyPicker ? DatePicker : WpDateTimePicker; - - return ( - - setInputStringAndMaybeCallOnChange( - newDateTimeISOString, - false - ) - } - is12Hour={ is12HourPicker } - /> - ); - } } - /> - ); -}; +); diff --git a/packages/js/components/src/experimental-select-control/README.md b/packages/js/components/src/experimental-select-control/README.md index 2b33d9457e8..b02c562f378 100644 --- a/packages/js/components/src/experimental-select-control/README.md +++ b/packages/js/components/src/experimental-select-control/README.md @@ -105,4 +105,6 @@ Name | Type | Default | Description `onInputChange` | Function | `() => null` | A callback that fires when the user input has changed `onRemove` | Function | `() => null` | A callback that fires when a selected item has been removed `onSelect` | Function | `() => null` | A callback that fires when an item has been selected -`selected` | Array or Item | `undefined` | An array of selected items or a single selected item +`selected` | Array or Item | `undefined` | An array of selected items or a single selected item\ +`onKeyDown` | Function | `() => null` | A callback that fires when a key is pressed +`readOnlyWhenClosed` | Boolean | `false` | Whether the input should be read-only when the menu is closed diff --git a/packages/js/components/src/experimental-select-control/combo-box.scss b/packages/js/components/src/experimental-select-control/combo-box.scss index 7faddd52a83..acb2426f6dc 100644 --- a/packages/js/components/src/experimental-select-control/combo-box.scss +++ b/packages/js/components/src/experimental-select-control/combo-box.scss @@ -18,7 +18,6 @@ .woocommerce-experimental-select-control__items-wrapper { display: flex; flex-grow: 1; - flex-wrap: wrap; align-items: center; padding: 2px $gap-smaller; diff --git a/packages/js/components/src/experimental-select-control/combo-box.tsx b/packages/js/components/src/experimental-select-control/combo-box.tsx index a71a323ff94..9bbbea87778 100644 --- a/packages/js/components/src/experimental-select-control/combo-box.tsx +++ b/packages/js/components/src/experimental-select-control/combo-box.tsx @@ -75,8 +75,8 @@ export const ComboBox = ( { { + inputRef.current = node; if ( typeof inputProps.ref === 'function' ) { - inputRef.current = node; ( inputProps.ref as unknown as ( node: HTMLInputElement | null diff --git a/packages/js/components/src/experimental-select-control/select-control.scss b/packages/js/components/src/experimental-select-control/select-control.scss index cf71dadd8cd..0ec18150b59 100644 --- a/packages/js/components/src/experimental-select-control/select-control.scss +++ b/packages/js/components/src/experimental-select-control/select-control.scss @@ -12,6 +12,18 @@ border-color: var( --wp-admin-theme-color ); } + &.is-read-only.is-multiple.has-selected-items { + .woocommerce-experimental-select-control__combo-box-wrapper { + cursor: default; + } + + .woocommerce-experimental-select-control__input { + opacity: 0; + width: 0; + height: 0; + } + } + &__label { display: inline-block; margin-bottom: $gap-smaller; diff --git a/packages/js/components/src/experimental-select-control/select-control.tsx b/packages/js/components/src/experimental-select-control/select-control.tsx index 7957e980d55..0bb76218a6e 100644 --- a/packages/js/components/src/experimental-select-control/select-control.tsx +++ b/packages/js/components/src/experimental-select-control/select-control.tsx @@ -9,13 +9,14 @@ import { useMultipleSelection, GetInputPropsOptions, } from 'downshift'; +import { useInstanceId } from '@wordpress/compose'; import { useState, useEffect, createElement, Fragment, } from '@wordpress/element'; -import { search } from '@wordpress/icons'; +import { chevronDown } from '@wordpress/icons'; /** * Internal dependencies @@ -57,6 +58,7 @@ export type SelectControlProps< ItemType > = { ) => void; onRemove?: ( item: ItemType ) => void; onSelect?: ( selected: ItemType ) => void; + onKeyDown?: ( e: KeyboardEvent ) => void; onFocus?: ( data: { inputValue: string } ) => void; stateReducer?: ( state: UseComboboxState< ItemType | null >, @@ -69,6 +71,8 @@ export type SelectControlProps< ItemType > = { inputProps?: GetInputPropsOptions; suffix?: JSX.Element | null; showToggleButton?: boolean; + readOnlyWhenClosed?: boolean; + /** * This is a feature already implemented in downshift@7.0.0 through the * reducer. In order for us to use it this prop is added temporarily until @@ -117,18 +121,24 @@ function SelectControl< ItemType = DefaultItemType >( { onRemove = () => null, onSelect = () => null, onFocus = () => null, + onKeyDown = () => null, stateReducer = ( state, actionAndChanges ) => actionAndChanges.changes, placeholder, selected, className, disabled, inputProps = {}, - suffix = , + suffix = , showToggleButton = false, + readOnlyWhenClosed = true, __experimentalOpenMenuOnFocus = false, }: SelectControlProps< ItemType > ) { const [ isFocused, setIsFocused ] = useState( false ); const [ inputValue, setInputValue ] = useState( '' ); + const instanceId = useInstanceId( + SelectControl, + 'woocommerce-experimental-select-control' + ); let selectedItems = selected === null ? [] : selected; selectedItems = Array.isArray( selectedItems ) @@ -230,15 +240,24 @@ function SelectControl< ItemType = DefaultItemType >( { }, } ); + const isEventOutside = ( event: React.FocusEvent ) => { + return ! document + .querySelector( '.' + instanceId ) + ?.contains( event.relatedTarget ); + }; + const onRemoveItem = ( item: ItemType ) => { selectItem( null ); removeSelectedItem( item ); onRemove( item ); }; + const isReadOnly = readOnlyWhenClosed && ! isOpen && ! isFocused; + const selectedItemTags = multiple ? ( ( { className={ classnames( 'woocommerce-experimental-select-control', className, + instanceId, { + 'is-read-only': isReadOnly, 'is-focused': isFocused, + 'is-multiple': multiple, + 'has-selected-items': selectedItems.length, } ) } > @@ -282,7 +305,12 @@ function SelectControl< ItemType = DefaultItemType >( { openMenu(); } }, - onBlur: () => setIsFocused( false ), + onBlur: ( event: React.FocusEvent ) => { + if ( isEventOutside( event ) ) { + setIsFocused( false ); + } + }, + onKeyDown, placeholder, disabled, ...inputProps, diff --git a/packages/js/components/src/experimental-select-control/selected-items.scss b/packages/js/components/src/experimental-select-control/selected-items.scss index 8f7c223483a..424bb40b9b9 100644 --- a/packages/js/components/src/experimental-select-control/selected-items.scss +++ b/packages/js/components/src/experimental-select-control/selected-items.scss @@ -1,3 +1,13 @@ +.woocommerce-experimental-select-control__selected-items { + flex-wrap: wrap; + + &.is-read-only { + font-size: 13px; + color: $gray-900; + font-family: var(--wp--preset--font-family--system-font); + } +} + .woocommerce-experimental-select-control__selected-item { margin-right: $gap-smallest; margin-top: 2px; diff --git a/packages/js/components/src/experimental-select-control/selected-items.tsx b/packages/js/components/src/experimental-select-control/selected-items.tsx index 65aab895f76..6f82039e640 100644 --- a/packages/js/components/src/experimental-select-control/selected-items.tsx +++ b/packages/js/components/src/experimental-select-control/selected-items.tsx @@ -1,7 +1,8 @@ /** * External dependencies */ -import { createElement, Fragment } from '@wordpress/element'; +import classnames from 'classnames'; +import { createElement } from '@wordpress/element'; /** * Internal dependencies @@ -10,6 +11,7 @@ import Tag from '../tag'; import { getItemLabelType, getItemValueType } from './types'; type SelectedItemsProps< ItemType > = { + isReadOnly: boolean; items: ItemType[]; getItemLabel: getItemLabelType< ItemType >; getItemValue: getItemValueType< ItemType >; @@ -22,14 +24,34 @@ type SelectedItemsProps< ItemType > = { }; export const SelectedItems = < ItemType, >( { + isReadOnly, items, getItemLabel, getItemValue, getSelectedItemProps, onRemove, }: SelectedItemsProps< ItemType > ) => { + const classes = classnames( + 'woocommerce-experimental-select-control__selected-items', + { + 'is-read-only': isReadOnly, + } + ); + + if ( isReadOnly ) { + return ( +
+ { items + .map( ( item ) => { + return getItemLabel( item ); + } ) + .join( ', ' ) } +
+ ); + } + return ( - <> +
{ items.map( ( item, index ) => { return ( // Disable reason: We prevent the default action to keep the input focused on click. @@ -42,6 +64,9 @@ export const SelectedItems = < ItemType, >( { selectedItem: item, index, } ) } + onMouseDown={ ( event ) => { + event.preventDefault(); + } } onClick={ ( event ) => { event.preventDefault(); } } @@ -56,6 +81,6 @@ export const SelectedItems = < ItemType, >( {
); } ) } - + ); }; diff --git a/packages/js/components/src/experimental-select-tree-control/select-tree-menu.tsx b/packages/js/components/src/experimental-select-tree-control/select-tree-menu.tsx index 294ceafef60..fe26480d94b 100644 --- a/packages/js/components/src/experimental-select-tree-control/select-tree-menu.tsx +++ b/packages/js/components/src/experimental-select-tree-control/select-tree-menu.tsx @@ -22,6 +22,7 @@ import { } from '../experimental-tree-control'; type MenuProps = { + isEventOutside: ( event: React.FocusEvent ) => boolean; isOpen: boolean; isLoading?: boolean; position?: Popover.Position; @@ -32,6 +33,7 @@ type MenuProps = { } & Omit< TreeControlProps, 'items' >; export const SelectTreeMenu = ( { + isEventOutside, isLoading, isOpen, className, @@ -103,8 +105,10 @@ export const SelectTreeMenu = ( { ) } position={ position } animate={ false } - onFocusOutside={ () => { - onClose(); + onFocusOutside={ ( event ) => { + if ( isEventOutside( event ) ) { + onClose(); + } } } > { isOpen && ( diff --git a/packages/js/components/src/experimental-select-tree-control/select-tree.tsx b/packages/js/components/src/experimental-select-tree-control/select-tree.tsx index 5c276342cc8..114e696326b 100644 --- a/packages/js/components/src/experimental-select-tree-control/select-tree.tsx +++ b/packages/js/components/src/experimental-select-tree-control/select-tree.tsx @@ -1,11 +1,11 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ /** * External dependencies */ -import { createElement, useState } from '@wordpress/element'; +import { chevronDown } from '@wordpress/icons'; import classNames from 'classnames'; -import { search } from '@wordpress/icons'; +import { createElement, useState } from '@wordpress/element'; import { useInstanceId } from '@wordpress/compose'; +import { BaseControl, TextControl } from '@wordpress/components'; /** * Internal dependencies @@ -19,8 +19,7 @@ import { SelectTreeMenu } from './select-tree-menu'; interface SelectTreeProps extends TreeControlProps { id: string; - selected?: Item[]; - getSelectedItemProps?: any; + selected?: Item | Item[]; treeRef?: React.ForwardedRef< HTMLOListElement >; suffix?: JSX.Element | null; isLoading?: boolean; @@ -30,9 +29,8 @@ interface SelectTreeProps extends TreeControlProps { export const SelectTree = function SelectTree( { items, - getSelectedItemProps, treeRef: ref, - suffix = , + suffix = , placeholder, isLoading, onInputChange, @@ -40,114 +38,171 @@ export const SelectTree = function SelectTree( { ...props }: SelectTreeProps ) { const linkedTree = useLinkedTree( items ); + const selectTreeInstanceId = useInstanceId( + SelectTree, + 'woocommerce-experimental-select-tree-control__dropdown' + ); const menuInstanceId = useInstanceId( SelectTree, 'woocommerce-select-tree-control__menu' ); + const isEventOutside = ( event: React.FocusEvent ) => { + return ! document + .querySelector( '.' + selectTreeInstanceId ) + ?.contains( event.relatedTarget ); + }; + + const recalculateInputValue = () => { + if ( onInputChange ) { + if ( ! props.multiple && props.selected ) { + onInputChange( ( props.selected as Item ).label ); + } else { + onInputChange( '' ); + } + } + }; + + const focusOnInput = () => { + ( + document.querySelector( `#${ props.id }-input` ) as HTMLInputElement + )?.focus(); + }; const [ isFocused, setIsFocused ] = useState( false ); const [ isOpen, setIsOpen ] = useState( false ); + const isReadOnly = ! isOpen && ! isFocused; + + const inputProps: React.InputHTMLAttributes< HTMLInputElement > = { + className: 'woocommerce-experimental-select-control__input', + id: `${ props.id }-input`, + 'aria-autocomplete': 'list', + 'aria-controls': `${ props.id }-menu`, + autoComplete: 'off', + onFocus: () => { + if ( ! isOpen ) { + setIsOpen( true ); + } + setIsFocused( true ); + }, + onBlur: ( event ) => { + if ( isOpen && isEventOutside( event ) ) { + setIsOpen( false ); + recalculateInputValue(); + } + setIsFocused( false ); + }, + onKeyDown: ( event ) => { + setIsOpen( true ); + if ( event.key === 'ArrowDown' ) { + event.preventDefault(); + // focus on the first element from the Popover + ( + document.querySelector( + `.${ menuInstanceId } input, .${ menuInstanceId } button` + ) as HTMLInputElement | HTMLButtonElement + )?.focus(); + } + if ( event.key === 'Tab' ) { + setIsOpen( false ); + recalculateInputValue(); + } + }, + onChange: ( event ) => + onInputChange && onInputChange( event.target.value ), + placeholder, + }; return (
- - { - if ( ! isOpen ) { - setIsOpen( true ); - } - setIsFocused( true ); - }, - onBlur: ( event ) => { - // if blurring to an element inside the dropdown, don't close it - if ( - isOpen && - ! document - .querySelector( '.' + menuInstanceId ) - ?.contains( event.relatedTarget ) - ) { - setIsOpen( false ); - } - setIsFocused( false ); - }, - onKeyDown: ( event ) => { - setIsOpen( true ); - if ( event.key === 'ArrowDown' ) { - event.preventDefault(); - // focus on the first element from the Popover - ( - document.querySelector( - `.${ menuInstanceId } input, .${ menuInstanceId } button` - ) as HTMLInputElement | HTMLButtonElement - )?.focus(); - } - if ( event.key === 'Tab' ) { - setIsOpen( false ); - } - }, - onChange: ( event ) => - onInputChange && - onInputChange( event.target.value ), - placeholder, - } } - suffix={ suffix } - > - item?.label || '' } - getItemValue={ ( item ) => item?.value || '' } - onRemove={ ( item ) => { - if ( ! Array.isArray( item ) && props.onRemove ) { - props.onRemove( item ); - setIsOpen( false ); - } - } } - getSelectedItemProps={ () => ( {} ) } - /> - + + { props.multiple ? ( + + item?.label || '' } + getItemValue={ ( item ) => item?.value || '' } + onRemove={ ( item ) => { + if ( + ! Array.isArray( item ) && + props.onRemove + ) { + props.onRemove( item ); + } + } } + getSelectedItemProps={ () => ( {} ) } + /> + + ) : ( + { + if ( onInputChange ) onInputChange( value ); + const item = items.find( + ( i ) => i.label === value + ); + if ( props.onSelect && item ) { + props.onSelect( item ); + } + if ( ! value && props.onRemove ) { + props.onRemove( props.selected as Item ); + } + } } + /> + ) } +
{ + if ( ! props.multiple && onInputChange ) { + onInputChange( ( item as Item ).label ); + setIsOpen( false ); + setIsFocused( false ); + focusOnInput(); + } + if ( props.onSelect ) { + props.onSelect( item ); + } + } } id={ `${ props.id }-menu` } className={ menuInstanceId.toString() } ref={ ref } + isEventOutside={ isEventOutside } isOpen={ isOpen } items={ linkedTree } shouldShowCreateButton={ shouldShowCreateButton } - onClose={ () => setIsOpen( false ) } + onClose={ () => { + setIsOpen( false ); + } } />
); diff --git a/packages/js/components/src/experimental-select-tree-control/stories/index.tsx b/packages/js/components/src/experimental-select-tree-control/stories/index.tsx index 024f1e68e9e..3d6fad1cf13 100644 --- a/packages/js/components/src/experimental-select-tree-control/stories/index.tsx +++ b/packages/js/components/src/experimental-select-tree-control/stories/index.tsx @@ -162,6 +162,36 @@ export const SingleWithinModalUsingBodyDropdownPlacement: React.FC = () => { ); }; +export const SingleSelectTree: React.FC = () => { + const [ value, setValue ] = React.useState( '' ); + const [ selected, setSelected ] = React.useState< Item | undefined >(); + + const items = filterItems( listItems, value ); + + return ( + + ! value || + listItems.findIndex( ( item ) => item.label === typedValue ) === + -1 + } + createValue={ value } + // eslint-disable-next-line no-alert + onCreateNew={ () => alert( 'create new called' ) } + onInputChange={ ( a ) => setValue( a || '' ) } + onSelect={ ( selectedItems ) => { + setSelected( selectedItems as Item ); + } } + onRemove={ () => setSelected( undefined ) } + /> + ); +}; + export default { title: 'WooCommerce Admin/experimental/SelectTreeControl', component: SelectTree, diff --git a/packages/js/components/src/experimental-tree-control/hooks/use-selection.ts b/packages/js/components/src/experimental-tree-control/hooks/use-selection.ts index 79e47bfc9ec..6ad08a558a5 100644 --- a/packages/js/components/src/experimental-tree-control/hooks/use-selection.ts +++ b/packages/js/components/src/experimental-tree-control/hooks/use-selection.ts @@ -122,8 +122,6 @@ export function useSelection( { if ( item.children.length && ! shouldNotRecursivelySelect ) { value.push( ...getDeepChildren( item ) ); } - } else if ( item.children?.length ) { - return; } if ( checked ) { diff --git a/packages/js/components/src/experimental-tree-control/tree-item.scss b/packages/js/components/src/experimental-tree-control/tree-item.scss index c3f51d28813..4f849e68fd1 100644 --- a/packages/js/components/src/experimental-tree-control/tree-item.scss +++ b/packages/js/components/src/experimental-tree-control/tree-item.scss @@ -45,10 +45,6 @@ $control-size: $gap-large; margin: 0; } - .components-radio-control__input { - @include screen-reader-only(); - } - .components-checkbox-control__label { display: none; } @@ -86,4 +82,8 @@ $control-size: $gap-large; min-width: $control-size; } } + + &__checkbox { + @include screen-reader-only(); + } } diff --git a/packages/js/components/src/experimental-tree-control/tree-item.tsx b/packages/js/components/src/experimental-tree-control/tree-item.tsx index e7e487d7992..6f87e716b90 100644 --- a/packages/js/components/src/experimental-tree-control/tree-item.tsx +++ b/packages/js/components/src/experimental-tree-control/tree-item.tsx @@ -60,13 +60,11 @@ export const TreeItem = forwardRef( function ForwardedTreeItem( /> ) : ( - selection.onSelectChild( - event.currentTarget.checked - ) + selection.onSelectChild( event.target.checked ) } /> ) } diff --git a/packages/js/components/src/experimental-tree-control/tree.tsx b/packages/js/components/src/experimental-tree-control/tree.tsx index 491c6c161bd..48490d66c23 100644 --- a/packages/js/components/src/experimental-tree-control/tree.tsx +++ b/packages/js/components/src/experimental-tree-control/tree.tsx @@ -33,34 +33,36 @@ export const Tree = forwardRef( function ForwardedTree( return ( <> -
    - { items.map( ( child, index ) => ( - { - ( - rootListRef.current - ?.closest( 'ol[role="tree"]' ) - ?.parentElement?.querySelector( - '.experimental-woocommerce-tree__button' - ) as HTMLButtonElement - )?.focus(); - } } - /> - ) ) } -
+ { items.length || isCreateButtonVisible ? ( +
    + { items.map( ( child, index ) => ( + { + ( + rootListRef.current + ?.closest( 'ol[role="tree"]' ) + ?.parentElement?.querySelector( + '.experimental-woocommerce-tree__button' + ) as HTMLButtonElement + )?.focus(); + } } + /> + ) ) } +
+ ) : null } { isCreateButtonVisible && ( @@ -196,50 +274,4 @@ class List extends Component { } } -List.propTypes = { - /** - * ID of the main SelectControl instance. - */ - instanceId: PropTypes.number, - /** - * ID used for a11y in the listbox. - */ - listboxId: PropTypes.string, - /** - * Parent node to bind keyboard events to. - */ - // eslint-disable-next-line no-undef - node: PropTypes.instanceOf( Element ).isRequired, - /** - * Function to execute when an option is selected. - */ - onSelect: PropTypes.func, - /** - * Array of options to display. - */ - options: PropTypes.arrayOf( - PropTypes.shape( { - isDisabled: PropTypes.bool, - key: PropTypes.oneOfType( [ PropTypes.number, PropTypes.string ] ) - .isRequired, - keywords: PropTypes.arrayOf( - PropTypes.oneOfType( [ PropTypes.string, PropTypes.number ] ) - ), - label: PropTypes.oneOfType( [ - PropTypes.string, - PropTypes.object, - ] ), - value: PropTypes.any, - } ) - ).isRequired, - /** - * Integer for the currently selected item. - */ - selectedIndex: PropTypes.number, - /** - * Bool to determine if the list should be positioned absolutely or staticly. - */ - staticList: PropTypes.bool, -}; - export default List; diff --git a/packages/js/components/src/select-control/stories/index.js b/packages/js/components/src/select-control/stories/index.tsx similarity index 95% rename from packages/js/components/src/select-control/stories/index.js rename to packages/js/components/src/select-control/stories/index.tsx index 9e0d5b0107f..292fb08f5a1 100644 --- a/packages/js/components/src/select-control/stories/index.js +++ b/packages/js/components/src/select-control/stories/index.tsx @@ -1,8 +1,12 @@ /** * External dependencies */ -import { SelectControl } from '@woocommerce/components'; -import { useState } from '@wordpress/element'; +import React from 'react'; +import { createElement, useState } from '@wordpress/element'; +/** + * Internal dependencies + */ +import SelectControl from '../'; const options = [ { diff --git a/packages/js/components/src/select-control/tags.js b/packages/js/components/src/select-control/tags.tsx similarity index 80% rename from packages/js/components/src/select-control/tags.js rename to packages/js/components/src/select-control/tags.tsx index 296e27bd4b2..e1d33a5b247 100644 --- a/packages/js/components/src/select-control/tags.js +++ b/packages/js/components/src/select-control/tags.tsx @@ -5,19 +5,36 @@ import { __, sprintf } from '@wordpress/i18n'; import { Button } from '@wordpress/components'; import { Icon, cancelCircleFilled } from '@wordpress/icons'; import { createElement, Component, Fragment } from '@wordpress/element'; -import { findIndex } from 'lodash'; -import PropTypes from 'prop-types'; +import { findIndex, isArray } from 'lodash'; /** * Internal dependencies */ import Tag from '../tag'; +import { Option, Selected } from './types'; + +type Props = { + /** + * Function called when selected results change, passed result list. + */ + onChange: ( selected: Option[] ) => void; + /** + * An array of objects describing selected values. If the label of the selected + * value is omitted, the Tag of that value will not be rendered inside the + * search box. + */ + selected?: Selected; + /** + * Render a 'Clear' button next to the input box to remove its contents. + */ + showClearButton?: boolean; +}; /** * A list of tags to display selected items. */ -class Tags extends Component { - constructor( props ) { +class Tags extends Component< Props > { + constructor( props: Props ) { super( props ); this.removeAll = this.removeAll.bind( this ); this.removeResult = this.removeResult.bind( this ); @@ -28,9 +45,13 @@ class Tags extends Component { onChange( [] ); } - removeResult( key ) { + removeResult( key: string | undefined ) { return () => { const { selected, onChange } = this.props; + if ( ! isArray( selected ) ) { + return; + } + const i = findIndex( selected, { key } ); onChange( [ ...selected.slice( 0, i ), @@ -41,7 +62,7 @@ class Tags extends Component { render() { const { selected, showClearButton } = this.props; - if ( ! selected.length ) { + if ( ! isArray( selected ) || ! selected.length ) { return null; } @@ -63,6 +84,7 @@ class Tags extends Component { key={ item.key } id={ item.key } label={ item.label } + // @ts-expect-error key is a string or undefined here remove={ this.removeResult } screenReaderLabel={ screenReaderLabel } /> @@ -89,31 +111,4 @@ class Tags extends Component { } } -Tags.propTypes = { - /** - * Function called when selected results change, passed result list. - */ - onChange: PropTypes.func, - /** - * Function to execute when an option is selected. - */ - onSelect: PropTypes.func, - /** - * An array of objects describing selected values. If the label of the selected - * value is omitted, the Tag of that value will not be rendered inside the - * search box. - */ - selected: PropTypes.arrayOf( - PropTypes.shape( { - key: PropTypes.oneOfType( [ PropTypes.number, PropTypes.string ] ) - .isRequired, - label: PropTypes.string, - } ) - ), - /** - * Render a 'Clear' button next to the input box to remove its contents. - */ - showClearButton: PropTypes.bool, -}; - export default Tags; diff --git a/packages/js/components/src/select-control/test/index.js b/packages/js/components/src/select-control/test/index.tsx similarity index 95% rename from packages/js/components/src/select-control/test/index.js rename to packages/js/components/src/select-control/test/index.tsx index 0cb6bfeb849..825fe0bdb1e 100644 --- a/packages/js/components/src/select-control/test/index.js +++ b/packages/js/components/src/select-control/test/index.tsx @@ -1,6 +1,7 @@ /** * External dependencies */ +import React from 'react'; import { render, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { createElement } from '@wordpress/element'; @@ -9,10 +10,11 @@ import { createElement } from '@wordpress/element'; * Internal dependencies */ import { SelectControl } from '../index'; +import { Option } from '../types'; describe( 'SelectControl', () => { const query = 'lorem'; - const options = [ + const options: Option[] = [ { key: '1', label: 'lorem 1', value: { id: '1' } }, { key: '2', label: 'lorem 2', value: { id: '2' } }, { key: '3', label: 'bar', value: { id: '3' } }, @@ -168,9 +170,9 @@ describe( 'SelectControl', () => { } ); it( 'changes the options on search', async () => { - const queriedOptions = []; + const queriedOptions: Option[] = []; // eslint-disable-next-line @typescript-eslint/no-shadow - const queryOptions = ( options, searchedQuery ) => { + const queryOptions = async ( options: Option[], searchedQuery: string | null ) => { if ( searchedQuery === 'test' ) { queriedOptions.push( { key: 'test-option', @@ -209,7 +211,7 @@ describe( 'SelectControl', () => { isSearchable showAllOnFocus options={ options } - onSearch={ () => options } + onSearch={ async () => options } onFilter={ () => options } onChange={ onChangeMock } /> @@ -229,7 +231,7 @@ describe( 'SelectControl', () => { isSearchable selected={ [ { ...options[ 0 ] } ] } options={ options } - onSearch={ () => options } + onSearch={ async () => options } onFilter={ () => options } onChange={ onChangeMock } /> @@ -258,7 +260,7 @@ describe( 'SelectControl', () => { isSearchable selected={ options[ 0 ].key } options={ options } - onSearch={ () => options } + onSearch={ async () => options } onFilter={ () => options } onChange={ onChangeMock } /> @@ -289,7 +291,7 @@ describe( 'SelectControl', () => { showAllOnFocus selected={ options[ 2 ].key } options={ options } - onSearch={ () => options } + onSearch={ async () => options } onFilter={ () => options } onChange={ onChangeMock } excludeSelectedOptions={ false } @@ -316,7 +318,7 @@ describe( 'SelectControl', () => { showAllOnFocus selected={ options[ 2 ].key } options={ options } - onSearch={ () => options } + onSearch={ async () => options } onFilter={ () => options } onChange={ onChangeMock } excludeSelectedOptions={ false } @@ -364,7 +366,7 @@ describe( 'SelectControl', () => { isSearchable selected={ [ { ...options[ 0 ] } ] } options={ options } - onSearch={ () => options } + onSearch={ async () => options } onFilter={ () => options } onChange={ onChangeMock } /> @@ -383,7 +385,7 @@ describe( 'SelectControl', () => { isSearchable selected={ options[ 0 ].key } options={ options } - onSearch={ () => options } + onSearch={ async () => options } onFilter={ () => options } onChange={ onChangeMock } /> @@ -416,9 +418,8 @@ describe( 'SelectControl', () => { const { getByRole } = render( options } + onSearch={ async () => options } onFilter={ () => options } - selected={ null } /> ); @@ -440,7 +441,7 @@ describe( 'SelectControl', () => { const { getByRole } = render( options } + onSearch={ async () => options } onFilter={ () => options } selected={ options[ 1 ].key } excludeSelectedOptions={ false } @@ -465,7 +466,7 @@ describe( 'SelectControl', () => { const { getByRole } = render( options } + onSearch={ async () => options } onFilter={ () => options } selected={ options[ options.length - 1 ].key } excludeSelectedOptions={ false } @@ -490,9 +491,8 @@ describe( 'SelectControl', () => { const { getByRole } = render( options } + onSearch={ async () => options } onFilter={ () => options } - selected={ null } /> ); @@ -514,9 +514,8 @@ describe( 'SelectControl', () => { const { getByRole, queryByRole } = render( options } + onSearch={ async () => options } onFilter={ () => options } - selected={ null } /> ); @@ -537,9 +536,8 @@ describe( 'SelectControl', () => { const { getByRole } = render( options } + onSearch={ async () => options } onFilter={ () => options } - selected={ null } excludeSelectedOptions={ false } onChange={ onChangeMock } /> diff --git a/packages/js/components/src/select-control/types.ts b/packages/js/components/src/select-control/types.ts new file mode 100644 index 00000000000..6744615cdf6 --- /dev/null +++ b/packages/js/components/src/select-control/types.ts @@ -0,0 +1,9 @@ +export type Option = { + key: string; + label: string; + isDisabled?: boolean; + keywords?: Array< string >; + value?: unknown; +}; + +export type Selected = string | Option[]; diff --git a/packages/js/components/src/sortable/sortable.tsx b/packages/js/components/src/sortable/sortable.tsx index c1e1f4e78f6..fbc175d45e5 100644 --- a/packages/js/components/src/sortable/sortable.tsx +++ b/packages/js/components/src/sortable/sortable.tsx @@ -110,7 +110,7 @@ export const Sortable = ( { // Items before the current item cause a one off error when // removed from the old array and spliced into the new array. - // TODO: Issue with dragging into same position having to do with isBefore returning true intially. + // TODO: Issue with dragging into same position having to do with isBefore returning true initially. let targetIndex = dragIndex < index ? index : index + 1; if ( isBefore( event, isHorizontal ) ) { targetIndex--; diff --git a/packages/js/components/src/table/README.md b/packages/js/components/src/table/README.md index 54378b6dd25..2048d89a3b6 100644 --- a/packages/js/components/src/table/README.md +++ b/packages/js/components/src/table/README.md @@ -61,7 +61,7 @@ Name | Type | Default | Description `ids` | Array | `null` | A list of IDs, matching to the row list so that ids[ 0 ] contains the object ID for the object displayed in row[ 0 ] `isLoading` | Boolean | `false` | Defines if the table contents are loading. It will display `TablePlaceholder` component instead of `Table` if that's the case `onQueryChange` | Function | `noop` | A function which returns a callback function to update the query string for a given `param` -`onColumnsChange` | Function | `noop` | A function which returns a callback function which is called upon the user changing the visiblity of columns +`onColumnsChange` | Function | `noop` | A function which returns a callback function which is called upon the user changing the visibility of columns `onSearch` | Function | `noop` | A function which is called upon the user searching in the table header `onSort` | Function | `undefined` | A function which is called upon the user changing the sorting of the table `downloadable` | Boolean | `false` | Whether the table must be downloadable. If true, the download button will appear diff --git a/packages/js/components/src/table/types.ts b/packages/js/components/src/table/types.ts index 79b61e92a3d..6bd3b086894 100644 --- a/packages/js/components/src/table/types.ts +++ b/packages/js/components/src/table/types.ts @@ -158,7 +158,7 @@ export type TableCardProps = CommonTableProps & { // eslint-disable-next-line @typescript-eslint/no-explicit-any onQueryChange?: ( param: string ) => ( ...props: any ) => void; /** - * A function which returns a callback function which is called upon the user changing the visiblity of columns. + * A function which returns a callback function which is called upon the user changing the visibility of columns. */ onColumnsChange?: ( showCols: Array< string >, key?: string ) => void; /** diff --git a/packages/js/data/PREVIOUS_CHANGELOG.md b/packages/js/data/PREVIOUS_CHANGELOG.md index 2bb5eb68baf..e0886007ce1 100644 --- a/packages/js/data/PREVIOUS_CHANGELOG.md +++ b/packages/js/data/PREVIOUS_CHANGELOG.md @@ -22,7 +22,7 @@ ## Breaking changes - Fix the batch fetch logic for the options data store. #7587 -- Add backwards compability for old function format. #7688 +- Add backwards compatibility for old function format. #7688 - Add console warning for inbox note contents exceeding 320 characters and add dompurify dependency. #7869 - Fix race condition in data package's options module. #7947 - Remove dev dependency `@woocommerce/wc-admin-settings`. #8057 diff --git a/packages/js/data/changelog/dev-add-resolve-select-types b/packages/js/data/changelog/dev-add-resolve-select-types new file mode 100644 index 00000000000..db3099c1ce9 --- /dev/null +++ b/packages/js/data/changelog/dev-add-resolve-select-types @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Added types for resolveSelect where applicable. diff --git a/packages/js/data/changelog/fix-37502 b/packages/js/data/changelog/fix-37502 new file mode 100644 index 00000000000..115429c4b01 --- /dev/null +++ b/packages/js/data/changelog/fix-37502 @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak + +Correct spelling errors diff --git a/packages/js/data/changelog/update-migrate-purchase-task-to-ts b/packages/js/data/changelog/update-migrate-purchase-task-to-ts new file mode 100644 index 00000000000..c8ea79bf545 --- /dev/null +++ b/packages/js/data/changelog/update-migrate-purchase-task-to-ts @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Fix onboarding productTypes TS define type diff --git a/packages/js/data/changelog/update-payfast b/packages/js/data/changelog/update-payfast new file mode 100644 index 00000000000..40e5dec5fce --- /dev/null +++ b/packages/js/data/changelog/update-payfast @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Update Payfast title diff --git a/packages/js/data/src/countries/index.ts b/packages/js/data/src/countries/index.ts index de2e1f9d298..434a7812c65 100644 --- a/packages/js/data/src/countries/index.ts +++ b/packages/js/data/src/countries/index.ts @@ -14,6 +14,7 @@ import * as actions from './actions'; import * as resolvers from './resolvers'; import reducer, { State } from './reducer'; import { WPDataSelectors } from '../types'; +import { PromiseifySelectors } from '../types/promiseify-selectors'; export * from './types'; export type { State }; @@ -35,4 +36,7 @@ declare module '@wordpress/data' { function select( key: typeof STORE_NAME ): SelectFromMap< typeof selectors > & WPDataSelectors; + function resolveSelect( + key: typeof STORE_NAME + ): PromiseifySelectors< SelectFromMap< typeof selectors > >; } diff --git a/packages/js/data/src/export/index.ts b/packages/js/data/src/export/index.ts index 278aee7dd12..f4cf1b2f874 100644 --- a/packages/js/data/src/export/index.ts +++ b/packages/js/data/src/export/index.ts @@ -13,6 +13,7 @@ import * as actions from './actions'; import reducer, { State } from './reducer'; import { WPDataSelectors } from '../types'; import controls from '../controls'; +import { PromiseifySelectors } from '../types/promiseify-selectors'; export * from './types'; export type { State }; @@ -33,4 +34,7 @@ declare module '@wordpress/data' { function select( key: typeof STORE_NAME ): SelectFromMap< typeof selectors > & WPDataSelectors; + function resolveSelect( + key: typeof STORE_NAME + ): PromiseifySelectors< SelectFromMap< typeof selectors > >; } diff --git a/packages/js/data/src/import/index.ts b/packages/js/data/src/import/index.ts index 327b16e91aa..78703711f8e 100644 --- a/packages/js/data/src/import/index.ts +++ b/packages/js/data/src/import/index.ts @@ -14,6 +14,7 @@ import * as actions from './actions'; import * as resolvers from './resolvers'; import reducer, { State } from './reducer'; import { WPDataSelectors } from '../types'; +import { PromiseifySelectors } from '../types/promiseify-selectors'; export * from './types'; export type { State }; @@ -35,4 +36,7 @@ declare module '@wordpress/data' { function select( key: typeof STORE_NAME ): SelectFromMap< typeof selectors > & WPDataSelectors; + function resolveSelect( + key: typeof STORE_NAME + ): PromiseifySelectors< SelectFromMap< typeof selectors > >; } diff --git a/packages/js/data/src/items/index.ts b/packages/js/data/src/items/index.ts index bd6cb4938ae..8864c21e8db 100644 --- a/packages/js/data/src/items/index.ts +++ b/packages/js/data/src/items/index.ts @@ -15,6 +15,7 @@ import reducer, { State } from './reducer'; import controls from '../controls'; import { WPDataActions, WPDataSelectors } from '../types'; import { getItemsType } from './selectors'; +import { PromiseifySelectors } from '../types/promiseify-selectors'; export * from './types'; export type { State }; @@ -40,4 +41,7 @@ declare module '@wordpress/data' { key: typeof STORE_NAME ): DispatchFromMap< typeof actions & WPDataActions >; function select( key: typeof STORE_NAME ): ItemsSelector; + function resolveSelect( + key: typeof STORE_NAME + ): PromiseifySelectors< ItemsSelector >; } diff --git a/packages/js/data/src/navigation/index.ts b/packages/js/data/src/navigation/index.ts index 7e3b8155a0a..32983bb57cd 100644 --- a/packages/js/data/src/navigation/index.ts +++ b/packages/js/data/src/navigation/index.ts @@ -16,6 +16,7 @@ import reducer, { State } from './reducer'; import * as resolvers from './resolvers'; import initDispatchers from './dispatchers'; import { WPDataActions, WPDataSelectors } from '../types'; +import { PromiseifySelectors } from '../types/promiseify-selectors'; registerStore< State >( STORE_NAME, { reducer: reducer as Reducer< State, AnyAction >, @@ -35,4 +36,7 @@ declare module '@wordpress/data' { function select( key: typeof STORE_NAME ): SelectFromMap< typeof selectors > & WPDataSelectors; + function resolveSelect( + key: typeof STORE_NAME + ): PromiseifySelectors< SelectFromMap< typeof selectors > >; } diff --git a/packages/js/data/src/notes/index.ts b/packages/js/data/src/notes/index.ts index 852489a696d..8543d3554eb 100644 --- a/packages/js/data/src/notes/index.ts +++ b/packages/js/data/src/notes/index.ts @@ -15,6 +15,7 @@ import * as selectors from './selectors'; import * as actions from './actions'; import * as resolvers from './resolvers'; import reducer, { State } from './reducer'; +import { PromiseifySelectors } from '../types/promiseify-selectors'; export * from './types'; export type { State }; @@ -36,4 +37,7 @@ declare module '@wordpress/data' { function select( key: typeof STORE_NAME ): SelectFromMap< typeof selectors > & WPDataSelectors; + function resolveSelect( + key: typeof STORE_NAME + ): PromiseifySelectors< SelectFromMap< typeof selectors > >; } diff --git a/packages/js/data/src/onboarding/actions.ts b/packages/js/data/src/onboarding/actions.ts index 8628f22031c..e46692dfca5 100644 --- a/packages/js/data/src/onboarding/actions.ts +++ b/packages/js/data/src/onboarding/actions.ts @@ -16,7 +16,7 @@ import { ProfileItems, TaskListType, TaskType, - OnboardingProductType, + OnboardingProductTypes, } from './types'; import { Plugin } from '../plugins/types'; @@ -267,9 +267,7 @@ export function actionTaskSuccess( task: Partial< TaskType > ) { }; } -export function getProductTypesSuccess( - productTypes: OnboardingProductType[] -) { +export function getProductTypesSuccess( productTypes: OnboardingProductTypes ) { return { type: TYPES.GET_PRODUCT_TYPES_SUCCESS, productTypes, diff --git a/packages/js/data/src/onboarding/index.ts b/packages/js/data/src/onboarding/index.ts index a1e5b73193d..c0af6023e7f 100644 --- a/packages/js/data/src/onboarding/index.ts +++ b/packages/js/data/src/onboarding/index.ts @@ -14,6 +14,7 @@ import * as actions from './actions'; import * as resolvers from './resolvers'; import reducer, { State } from './reducer'; import { WPDataActions, WPDataSelectors } from '../types'; +import { PromiseifySelectors } from '../types/promiseify-selectors'; export * from './types'; export type { State }; @@ -35,4 +36,7 @@ declare module '@wordpress/data' { key: typeof STORE_NAME ): DispatchFromMap< typeof actions & WPDataActions >; function select( key: typeof STORE_NAME ): OnboardingSelector; + function resolveSelect( + key: typeof STORE_NAME + ): PromiseifySelectors< OnboardingSelector >; } diff --git a/packages/js/data/src/onboarding/reducer.ts b/packages/js/data/src/onboarding/reducer.ts index a3b32468989..da69fae2c92 100644 --- a/packages/js/data/src/onboarding/reducer.ts +++ b/packages/js/data/src/onboarding/reducer.ts @@ -35,7 +35,7 @@ export const defaultState: OnboardingState = { }, emailPrefill: '', paymentMethods: [], - productTypes: [], + productTypes: {}, requesting: {}, taskLists: {}, }; diff --git a/packages/js/data/src/onboarding/resolvers.ts b/packages/js/data/src/onboarding/resolvers.ts index 5bb69f5492b..8674a4e7d83 100644 --- a/packages/js/data/src/onboarding/resolvers.ts +++ b/packages/js/data/src/onboarding/resolvers.ts @@ -24,7 +24,7 @@ import { import { DeprecatedTasks } from './deprecated-tasks'; import { ExtensionList, - OnboardingProductType, + OnboardingProductTypes, ProfileItems, TaskListType, } from './types'; @@ -126,7 +126,7 @@ export function* getFreeExtensions() { export function* getProductTypes() { try { - const results: OnboardingProductType[] = yield apiFetch( { + const results: OnboardingProductTypes = yield apiFetch( { path: WC_ADMIN_NAMESPACE + '/onboarding/product-types', method: 'GET', } ); diff --git a/packages/js/data/src/onboarding/selectors.ts b/packages/js/data/src/onboarding/selectors.ts index b73c1111211..508886fbfb7 100644 --- a/packages/js/data/src/onboarding/selectors.ts +++ b/packages/js/data/src/onboarding/selectors.ts @@ -12,7 +12,6 @@ import { OnboardingState, ExtensionList, ProfileItems, - OnboardingProductType, } from './types'; import { WPDataSelectors } from '../types'; import { Plugin } from '../plugins/types'; @@ -92,10 +91,8 @@ export const getEmailPrefill = ( state: OnboardingState ): string => { return state.emailPrefill || ''; }; -export const getProductTypes = ( - state: OnboardingState -): OnboardingProductType[] => { - return state.productTypes || []; +export const getProductTypes = ( state: OnboardingState ) => { + return state.productTypes || {}; }; export type OnboardingSelectors = { diff --git a/packages/js/data/src/onboarding/types.ts b/packages/js/data/src/onboarding/types.ts index 953ecf5ce0c..217cac646b0 100644 --- a/packages/js/data/src/onboarding/types.ts +++ b/packages/js/data/src/onboarding/types.ts @@ -75,7 +75,7 @@ export type OnboardingState = { profileItems: ProfileItems; taskLists: Record< string, TaskListType >; paymentMethods: Plugin[]; - productTypes: OnboardingProductType[]; + productTypes: OnboardingProductTypes; emailPrefill: string; // TODO clarify what the error record's type is errors: Record< string, unknown >; @@ -152,11 +152,21 @@ export type MethodFields = { }; export type OnboardingProductType = { - default?: boolean; label: string; + default?: boolean; product?: number; + id?: number; + title?: string; + yearly_price?: number; + description?: string; + more_url?: string; + slug?: string; }; +export type OnboardingProductTypes = + | Record< ProductTypeSlug, OnboardingProductType > + | Record< string, never >; + export type ExtensionList = { key: string; title: string; diff --git a/packages/js/data/src/options/index.ts b/packages/js/data/src/options/index.ts index 3b0d282f805..3af31a4fcbb 100644 --- a/packages/js/data/src/options/index.ts +++ b/packages/js/data/src/options/index.ts @@ -14,6 +14,7 @@ import * as resolvers from './resolvers'; import reducer, { State } from './reducer'; import { controls } from './controls'; import { WPDataActions, WPDataSelectors } from '../types'; +import { PromiseifySelectors } from '../types/promiseify-selectors'; export * from './types'; export type { State }; @@ -34,4 +35,7 @@ declare module '@wordpress/data' { function select( key: typeof STORE_NAME ): SelectFromMap< typeof selectors > & WPDataSelectors; + function resolveSelect( + key: typeof STORE_NAME + ): PromiseifySelectors< SelectFromMap< typeof selectors > >; } diff --git a/packages/js/data/src/payment-gateways/index.ts b/packages/js/data/src/payment-gateways/index.ts index 609de07586d..b9c74e18f19 100644 --- a/packages/js/data/src/payment-gateways/index.ts +++ b/packages/js/data/src/payment-gateways/index.ts @@ -14,6 +14,7 @@ import * as selectors from './selectors'; import reducer from './reducer'; import { STORE_KEY } from './constants'; import { WPDataActions } from '../types'; +import { PromiseifySelectors } from '../types/promiseify-selectors'; export const PAYMENT_GATEWAYS_STORE_NAME = STORE_KEY; @@ -33,4 +34,7 @@ declare module '@wordpress/data' { function select( key: typeof STORE_KEY ): SelectFromMap< typeof selectors > & WPDataActions; + function resolveSelect( + key: typeof STORE_KEY + ): PromiseifySelectors< SelectFromMap< typeof selectors > >; } diff --git a/packages/js/data/src/plugins/constants.ts b/packages/js/data/src/plugins/constants.ts index a3949810cc2..511984b757b 100644 --- a/packages/js/data/src/plugins/constants.ts +++ b/packages/js/data/src/plugins/constants.ts @@ -33,7 +33,7 @@ export const pluginNames = { 'woocommerce' ), 'woocommerce-gateway-stripe': __( 'WooCommerce Stripe', 'woocommerce' ), - 'woocommerce-payfast-gateway': __( 'WooCommerce PayFast', 'woocommerce' ), + 'woocommerce-payfast-gateway': __( 'WooCommerce Payfast', 'woocommerce' ), 'woocommerce-payments': __( 'WooCommerce Payments', 'woocommerce' ), 'woocommerce-services': __( 'WooCommerce Shipping & Tax', 'woocommerce' ), 'woocommerce-services:shipping': __( diff --git a/packages/js/data/src/plugins/index.ts b/packages/js/data/src/plugins/index.ts index b209a420858..9bcc4fc6db8 100644 --- a/packages/js/data/src/plugins/index.ts +++ b/packages/js/data/src/plugins/index.ts @@ -13,6 +13,7 @@ import * as actions from './actions'; import * as resolvers from './resolvers'; import reducer, { State } from './reducer'; import { WPDataActions, WPDataSelectors } from '../types'; +import { PromiseifySelectors } from '../types/promiseify-selectors'; export * from './types'; export type { State }; @@ -33,4 +34,7 @@ declare module '@wordpress/data' { function select( key: typeof STORE_NAME ): SelectFromMap< typeof selectors > & WPDataSelectors; + function resolveSelect( + key: typeof STORE_NAME + ): PromiseifySelectors< SelectFromMap< typeof selectors > >; } diff --git a/packages/js/data/src/product-form/index.ts b/packages/js/data/src/product-form/index.ts index 0a6dfbef68d..60497f202f2 100644 --- a/packages/js/data/src/product-form/index.ts +++ b/packages/js/data/src/product-form/index.ts @@ -14,6 +14,7 @@ import * as actions from './actions'; import * as resolvers from './resolvers'; import reducer, { State } from './reducer'; import { WPDataSelectors } from '../types'; +import { PromiseifySelectors } from '../types/promiseify-selectors'; export * from './types'; export type { State }; @@ -34,4 +35,7 @@ declare module '@wordpress/data' { function select( key: typeof STORE_NAME ): SelectFromMap< typeof selectors > & WPDataSelectors; + function resolveSelect( + key: typeof STORE_NAME + ): PromiseifySelectors< SelectFromMap< typeof selectors > >; } diff --git a/packages/js/data/src/reports/index.ts b/packages/js/data/src/reports/index.ts index 7d738c15c78..277001992a7 100644 --- a/packages/js/data/src/reports/index.ts +++ b/packages/js/data/src/reports/index.ts @@ -24,6 +24,7 @@ import { ReportStatObjectInfer, ReportStatQueryParams, } from './types'; +import { PromiseifySelectors } from '../types/promiseify-selectors'; export * from './types'; export type { State }; @@ -57,4 +58,7 @@ declare module '@wordpress/data' { key: typeof STORE_NAME ): DispatchFromMap< typeof actions & WPDataActions >; function select( key: typeof STORE_NAME ): ReportsSelect; + function resolveSelect( + key: typeof STORE_NAME + ): PromiseifySelectors< ReportsSelect >; } diff --git a/packages/js/data/src/reviews/index.ts b/packages/js/data/src/reviews/index.ts index db73e910ab1..6a4463b57fc 100644 --- a/packages/js/data/src/reviews/index.ts +++ b/packages/js/data/src/reviews/index.ts @@ -16,6 +16,7 @@ import * as resolvers from './resolvers'; import controls from '../controls'; import reducer, { State } from './reducer'; import { WPDataActions, WPDataSelectors } from '../types'; +import { PromiseifySelectors } from '../types/promiseify-selectors'; export * from './types'; export type { State }; @@ -39,4 +40,7 @@ declare module '@wordpress/data' { function select( key: typeof STORE_NAME ): SelectFromMap< typeof selectors > & WPDataSelectors; + function resolveSelect( + key: typeof STORE_NAME + ): PromiseifySelectors< ReviewSelector >; } diff --git a/packages/js/data/src/settings/index.ts b/packages/js/data/src/settings/index.ts index cd21446c45c..d370aaef0bd 100644 --- a/packages/js/data/src/settings/index.ts +++ b/packages/js/data/src/settings/index.ts @@ -17,6 +17,7 @@ import * as actions from './actions'; import * as resolvers from './resolvers'; import reducer, { State } from './reducer'; import { SettingsState } from './types'; +import { PromiseifySelectors } from '../types/promiseify-selectors'; export * from './types'; export type { State }; @@ -37,4 +38,7 @@ declare module '@wordpress/data' { function select( key: typeof STORE_NAME ): SelectFromMap< typeof selectors > & WPDataSelectors; + function resolveSelect( + key: typeof STORE_NAME + ): PromiseifySelectors< SelectFromMap< typeof selectors > >; } diff --git a/packages/js/data/src/types/promiseify-selectors.ts b/packages/js/data/src/types/promiseify-selectors.ts new file mode 100644 index 00000000000..fc81e2d2734 --- /dev/null +++ b/packages/js/data/src/types/promiseify-selectors.ts @@ -0,0 +1,13 @@ +/** + * Type helper that maps select() return types to their resolveSelect() return types. + * This works by mapping over each Selector, and returning a function that + * returns a Promise of the Selector's return type. + */ + +export type PromiseifySelectors< Selectors > = { + [ SelectorFunction in keyof Selectors ]: Selectors[ SelectorFunction ] extends ( + ...args: infer SelectorArgs + ) => infer SelectorReturnType + ? ( ...args: SelectorArgs ) => Promise< SelectorReturnType > + : never; +}; diff --git a/packages/js/experimental/changelog/fix-37502 b/packages/js/experimental/changelog/fix-37502 new file mode 100644 index 00000000000..115429c4b01 --- /dev/null +++ b/packages/js/experimental/changelog/fix-37502 @@ -0,0 +1,4 @@ +Significance: patch +Type: tweak + +Correct spelling errors diff --git a/packages/js/experimental/src/experimental-list/test/index.tsx b/packages/js/experimental/src/experimental-list/test/index.tsx index c37e714bc76..89e8f6889a7 100644 --- a/packages/js/experimental/src/experimental-list/test/index.tsx +++ b/packages/js/experimental/src/experimental-list/test/index.tsx @@ -163,7 +163,7 @@ describe( 'Experimental List', () => { } ); describe( 'ExperimentalListItemCollapse', () => { - it( 'should not render its children intially, but an extra list footer with show text', () => { + it( 'should not render its children initially, but an extra list footer with show text', () => { const { container } = render( { ); } ); - it( 'should call onVisible when visiblity sensor calls it', () => { + it( 'should call onVisible when visibility sensor calls it', () => { const onVisible = jest.fn(); const { getByText } = render( { expect( onVisible ).toHaveBeenCalledWith( note ); } ); - it( 'should call onVisible when visiblity sensor calls it, but only once', () => { + it( 'should call onVisible when visibility sensor calls it, but only once', () => { const onVisible = jest.fn(); const { getByText } = render( { + if ( ! location ) { + return null; + } + + let match = null; + let matchSimilarity = minimumSimilarity; + // eslint-disable-next-line @wordpress/no-unused-vars-before-return -- don't want to put this inside the loop + const mappingRegion = getMappingRegion( location ); + + for ( const option of countryStateOptions ) { + // Country matches exactly. + if ( option.key === location.country_short ) { + return option; + } + + // Countries have regions such as 'US:CA'. + const countryCode = option.key.split( ':' )[ 0 ]; + if ( + countryCode === location.country_short && + option.label.includes( '—' ) + ) { + const wcRegion = option.label.split( '—' )[ 1 ].trim(); + + // Region matches exactly with mapping. + if ( mappingRegion === wcRegion ) { + return option; + } + + // Find the region with the highest similarity. + const similarity = Math.max( + stringSimilarity.compareTwoStrings( + wcRegion, + location.region || '' + ), + stringSimilarity.compareTwoStrings( + wcRegion, + location.city || '' + ) + ); + if ( similarity >= matchSimilarity ) { + match = option; + matchSimilarity = similarity; + } + } + } + return match; +}; diff --git a/packages/js/onboarding/src/utils/countries/location-mapping.ts b/packages/js/onboarding/src/utils/countries/location-mapping.ts new file mode 100644 index 00000000000..dce03dce1b4 --- /dev/null +++ b/packages/js/onboarding/src/utils/countries/location-mapping.ts @@ -0,0 +1,58 @@ +/** + * External dependencies + */ +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { Location } from '.'; + +/** + * This file is used to map a location to a WC region label + * so that we can find the correct country option + * since WPCOM geolocation returns a different + * region or city name. + */ + +// Key is the country code, value is an object with keys as region/city names and values as WC region labels. +const MAPPING: Record< string, Record< string, string > > = { + PH: { + 'National Capital Region': __( 'Metro Manila', 'woocommerce' ), + }, + IT: { + Rome: __( 'Roma', 'woocommerce' ), + }, +}; + +/** + * Returns a WC mapping region name for the given country, region and city. + */ +export const getMappingRegion = ( { + country_short: countryCode, + region = '', + city = '', +}: Location ) => { + if ( ! countryCode ) { + return null; + } + + const countryMapping = MAPPING[ countryCode ]; + if ( ! countryMapping ) { + return null; + } + + const regionMapping = countryMapping[ region ]; + if ( regionMapping ) { + return regionMapping; + } + + const cityMapping = countryMapping[ city ]; + if ( cityMapping ) { + return cityMapping; + } + + return null; +}; + +export default MAPPING; diff --git a/packages/js/onboarding/src/utils/countries/tests/utils/country-options.ts b/packages/js/onboarding/src/utils/countries/tests/utils/country-options.ts new file mode 100644 index 00000000000..36fd8d9c256 --- /dev/null +++ b/packages/js/onboarding/src/utils/countries/tests/utils/country-options.ts @@ -0,0 +1,8390 @@ +export const countryStateOptions = [ + { + key: 'AF', + label: 'Afghanistan', + }, + { + key: 'AX', + label: 'Åland Islands', + }, + { + key: 'AL:AL-01', + label: 'Albania — Berat', + }, + { + key: 'AL:AL-09', + label: 'Albania — Dibër', + }, + { + key: 'AL:AL-02', + label: 'Albania — Durrës', + }, + { + key: 'AL:AL-03', + label: 'Albania — Elbasan', + }, + { + key: 'AL:AL-04', + label: 'Albania — Fier', + }, + { + key: 'AL:AL-05', + label: 'Albania — Gjirokastër', + }, + { + key: 'AL:AL-06', + label: 'Albania — Korçë', + }, + { + key: 'AL:AL-07', + label: 'Albania — Kukës', + }, + { + key: 'AL:AL-08', + label: 'Albania — Lezhë', + }, + { + key: 'AL:AL-10', + label: 'Albania — Shkodër', + }, + { + key: 'AL:AL-11', + label: 'Albania — Tirana', + }, + { + key: 'AL:AL-12', + label: 'Albania — Vlorë', + }, + { + key: 'DZ:DZ-01', + label: 'Algeria — Adrar', + }, + { + key: 'DZ:DZ-02', + label: 'Algeria — Chlef', + }, + { + key: 'DZ:DZ-03', + label: 'Algeria — Laghouat', + }, + { + key: 'DZ:DZ-04', + label: 'Algeria — Oum El Bouaghi', + }, + { + key: 'DZ:DZ-05', + label: 'Algeria — Batna', + }, + { + key: 'DZ:DZ-06', + label: 'Algeria — Béjaïa', + }, + { + key: 'DZ:DZ-07', + label: 'Algeria — Biskra', + }, + { + key: 'DZ:DZ-08', + label: 'Algeria — Béchar', + }, + { + key: 'DZ:DZ-09', + label: 'Algeria — Blida', + }, + { + key: 'DZ:DZ-10', + label: 'Algeria — Bouira', + }, + { + key: 'DZ:DZ-11', + label: 'Algeria — Tamanghasset', + }, + { + key: 'DZ:DZ-12', + label: 'Algeria — Tébessa', + }, + { + key: 'DZ:DZ-13', + label: 'Algeria — Tlemcen', + }, + { + key: 'DZ:DZ-14', + label: 'Algeria — Tiaret', + }, + { + key: 'DZ:DZ-15', + label: 'Algeria — Tizi Ouzou', + }, + { + key: 'DZ:DZ-16', + label: 'Algeria — Algiers', + }, + { + key: 'DZ:DZ-17', + label: 'Algeria — Djelfa', + }, + { + key: 'DZ:DZ-18', + label: 'Algeria — Jijel', + }, + { + key: 'DZ:DZ-19', + label: 'Algeria — Sétif', + }, + { + key: 'DZ:DZ-20', + label: 'Algeria — Saïda', + }, + { + key: 'DZ:DZ-21', + label: 'Algeria — Skikda', + }, + { + key: 'DZ:DZ-22', + label: 'Algeria — Sidi Bel Abbès', + }, + { + key: 'DZ:DZ-23', + label: 'Algeria — Annaba', + }, + { + key: 'DZ:DZ-24', + label: 'Algeria — Guelma', + }, + { + key: 'DZ:DZ-25', + label: 'Algeria — Constantine', + }, + { + key: 'DZ:DZ-26', + label: 'Algeria — Médéa', + }, + { + key: 'DZ:DZ-27', + label: 'Algeria — Mostaganem', + }, + { + key: 'DZ:DZ-28', + label: 'Algeria — M’Sila', + }, + { + key: 'DZ:DZ-29', + label: 'Algeria — Mascara', + }, + { + key: 'DZ:DZ-30', + label: 'Algeria — Ouargla', + }, + { + key: 'DZ:DZ-31', + label: 'Algeria — Oran', + }, + { + key: 'DZ:DZ-32', + label: 'Algeria — El Bayadh', + }, + { + key: 'DZ:DZ-33', + label: 'Algeria — Illizi', + }, + { + key: 'DZ:DZ-34', + label: 'Algeria — Bordj Bou Arréridj', + }, + { + key: 'DZ:DZ-35', + label: 'Algeria — Boumerdès', + }, + { + key: 'DZ:DZ-36', + label: 'Algeria — El Tarf', + }, + { + key: 'DZ:DZ-37', + label: 'Algeria — Tindouf', + }, + { + key: 'DZ:DZ-38', + label: 'Algeria — Tissemsilt', + }, + { + key: 'DZ:DZ-39', + label: 'Algeria — El Oued', + }, + { + key: 'DZ:DZ-40', + label: 'Algeria — Khenchela', + }, + { + key: 'DZ:DZ-41', + label: 'Algeria — Souk Ahras', + }, + { + key: 'DZ:DZ-42', + label: 'Algeria — Tipasa', + }, + { + key: 'DZ:DZ-43', + label: 'Algeria — Mila', + }, + { + key: 'DZ:DZ-44', + label: 'Algeria — Aïn Defla', + }, + { + key: 'DZ:DZ-45', + label: 'Algeria — Naama', + }, + { + key: 'DZ:DZ-46', + label: 'Algeria — Aïn Témouchent', + }, + { + key: 'DZ:DZ-47', + label: 'Algeria — Ghardaïa', + }, + { + key: 'DZ:DZ-48', + label: 'Algeria — Relizane', + }, + { + key: 'AS', + label: 'American Samoa', + }, + { + key: 'AD', + label: 'Andorra', + }, + { + key: 'AO:BGO', + label: 'Angola — Bengo', + }, + { + key: 'AO:BLU', + label: 'Angola — Benguela', + }, + { + key: 'AO:BIE', + label: 'Angola — Bié', + }, + { + key: 'AO:CAB', + label: 'Angola — Cabinda', + }, + { + key: 'AO:CNN', + label: 'Angola — Cunene', + }, + { + key: 'AO:HUA', + label: 'Angola — Huambo', + }, + { + key: 'AO:HUI', + label: 'Angola — Huíla', + }, + { + key: 'AO:CCU', + label: 'Angola — Kuando Kubango', + }, + { + key: 'AO:CNO', + label: 'Angola — Kwanza-Norte', + }, + { + key: 'AO:CUS', + label: 'Angola — Kwanza-Sul', + }, + { + key: 'AO:LUA', + label: 'Angola — Luanda', + }, + { + key: 'AO:LNO', + label: 'Angola — Lunda-Norte', + }, + { + key: 'AO:LSU', + label: 'Angola — Lunda-Sul', + }, + { + key: 'AO:MAL', + label: 'Angola — Malanje', + }, + { + key: 'AO:MOX', + label: 'AngolaSevastopol — Moxico', + }, + { + key: 'AO:NAM', + label: 'Angola — Namibe', + }, + { + key: 'AO:UIG', + label: 'Angola — Uíge', + }, + { + key: 'AO:ZAI', + label: 'Angola — Zaire', + }, + { + key: 'AI', + label: 'Anguilla', + }, + { + key: 'AQ', + label: 'Antarctica', + }, + { + key: 'AG', + label: 'Antigua and Barbuda', + }, + { + key: 'AR:C', + label: 'Argentina — Ciudad Autónoma de Buenos Aires', + }, + { + key: 'AR:B', + label: 'Argentina — Buenos Aires', + }, + { + key: 'AR:K', + label: 'Argentina — Catamarca', + }, + { + key: 'AR:H', + label: 'Argentina — Chaco', + }, + { + key: 'AR:U', + label: 'Argentina — Chubut', + }, + { + key: 'AR:X', + label: 'Argentina — Córdoba', + }, + { + key: 'AR:W', + label: 'Argentina — Corrientes', + }, + { + key: 'AR:E', + label: 'Argentina — Entre Ríos', + }, + { + key: 'AR:P', + label: 'Argentina — Formosa', + }, + { + key: 'AR:Y', + label: 'Argentina — Jujuy', + }, + { + key: 'AR:L', + label: 'Argentina — La Pampa', + }, + { + key: 'AR:F', + label: 'Argentina — La Rioja', + }, + { + key: 'AR:M', + label: 'Argentina — Mendoza', + }, + { + key: 'AR:N', + label: 'Argentina — Misiones', + }, + { + key: 'AR:Q', + label: 'Argentina — Neuquén', + }, + { + key: 'AR:R', + label: 'Argentina — Río Negro', + }, + { + key: 'AR:A', + label: 'Argentina — Salta', + }, + { + key: 'AR:J', + label: 'Argentina — San Juan', + }, + { + key: 'AR:D', + label: 'Argentina — San Luis', + }, + { + key: 'AR:Z', + label: 'Argentina — Santa Cruz', + }, + { + key: 'AR:S', + label: 'Argentina — Santa Fe', + }, + { + key: 'AR:G', + label: 'Argentina — Santiago del Estero', + }, + { + key: 'AR:V', + label: 'Argentina — Tierra del Fuego', + }, + { + key: 'AR:T', + label: 'Argentina — Tucumán', + }, + { + key: 'AM', + label: 'Armenia', + }, + { + key: 'AW', + label: 'Aruba', + }, + { + key: 'AU:ACT', + label: 'Australia — Australian Capital Territory', + }, + { + key: 'AU:NSW', + label: 'Australia — New South Wales', + }, + { + key: 'AU:NT', + label: 'Australia — Northern Territory', + }, + { + key: 'AU:QLD', + label: 'Australia — Queensland', + }, + { + key: 'AU:SA', + label: 'Australia — South Australia', + }, + { + key: 'AU:TAS', + label: 'Australia — Tasmania', + }, + { + key: 'AU:VIC', + label: 'Australia — Victoria', + }, + { + key: 'AU:WA', + label: 'Australia — Western Australia', + }, + { + key: 'AT', + label: 'Austria', + }, + { + key: 'AZ', + label: 'Azerbaijan', + }, + { + key: 'BS', + label: 'Bahamas', + }, + { + key: 'BH', + label: 'Bahrain', + }, + { + key: 'BD:BD-05', + label: 'Bangladesh — Bagerhat', + }, + { + key: 'BD:BD-01', + label: 'Bangladesh — Bandarban', + }, + { + key: 'BD:BD-02', + label: 'Bangladesh — Barguna', + }, + { + key: 'BD:BD-06', + label: 'Bangladesh — Barishal', + }, + { + key: 'BD:BD-07', + label: 'Bangladesh — Bhola', + }, + { + key: 'BD:BD-03', + label: 'Bangladesh — Bogura', + }, + { + key: 'BD:BD-04', + label: 'Bangladesh — Brahmanbaria', + }, + { + key: 'BD:BD-09', + label: 'Bangladesh — Chandpur', + }, + { + key: 'BD:BD-10', + label: 'Bangladesh — Chattogram', + }, + { + key: 'BD:BD-12', + label: 'Bangladesh — Chuadanga', + }, + { + key: 'BD:BD-11', + label: "Bangladesh — Cox's Bazar", + }, + { + key: 'BD:BD-08', + label: 'Bangladesh — Cumilla', + }, + { + key: 'BD:BD-13', + label: 'Bangladesh — Dhaka', + }, + { + key: 'BD:BD-14', + label: 'Bangladesh — Dinajpur', + }, + { + key: 'BD:BD-15', + label: 'Bangladesh — Faridpur ', + }, + { + key: 'BD:BD-16', + label: 'Bangladesh — Feni', + }, + { + key: 'BD:BD-19', + label: 'Bangladesh — Gaibandha', + }, + { + key: 'BD:BD-18', + label: 'Bangladesh — Gazipur', + }, + { + key: 'BD:BD-17', + label: 'Bangladesh — Gopalganj', + }, + { + key: 'BD:BD-20', + label: 'Bangladesh — Habiganj', + }, + { + key: 'BD:BD-21', + label: 'Bangladesh — Jamalpur', + }, + { + key: 'BD:BD-22', + label: 'Bangladesh — Jashore', + }, + { + key: 'BD:BD-25', + label: 'Bangladesh — Jhalokati', + }, + { + key: 'BD:BD-23', + label: 'Bangladesh — Jhenaidah', + }, + { + key: 'BD:BD-24', + label: 'Bangladesh — Joypurhat', + }, + { + key: 'BD:BD-29', + label: 'Bangladesh — Khagrachhari', + }, + { + key: 'BD:BD-27', + label: 'Bangladesh — Khulna', + }, + { + key: 'BD:BD-26', + label: 'Bangladesh — Kishoreganj', + }, + { + key: 'BD:BD-28', + label: 'Bangladesh — Kurigram', + }, + { + key: 'BD:BD-30', + label: 'Bangladesh — Kushtia', + }, + { + key: 'BD:BD-31', + label: 'Bangladesh — Lakshmipur', + }, + { + key: 'BD:BD-32', + label: 'Bangladesh — Lalmonirhat', + }, + { + key: 'BD:BD-36', + label: 'Bangladesh — Madaripur', + }, + { + key: 'BD:BD-37', + label: 'Bangladesh — Magura', + }, + { + key: 'BD:BD-33', + label: 'Bangladesh — Manikganj ', + }, + { + key: 'BD:BD-39', + label: 'Bangladesh — Meherpur', + }, + { + key: 'BD:BD-38', + label: 'Bangladesh — Moulvibazar', + }, + { + key: 'BD:BD-35', + label: 'Bangladesh — Munshiganj', + }, + { + key: 'BD:BD-34', + label: 'Bangladesh — Mymensingh', + }, + { + key: 'BD:BD-48', + label: 'Bangladesh — Naogaon', + }, + { + key: 'BD:BD-43', + label: 'Bangladesh — Narail', + }, + { + key: 'BD:BD-40', + label: 'Bangladesh — Narayanganj', + }, + { + key: 'BD:BD-42', + label: 'Bangladesh — Narsingdi', + }, + { + key: 'BD:BD-44', + label: 'Bangladesh — Natore', + }, + { + key: 'BD:BD-45', + label: 'Bangladesh — Nawabganj', + }, + { + key: 'BD:BD-41', + label: 'Bangladesh — Netrakona', + }, + { + key: 'BD:BD-46', + label: 'Bangladesh — Nilphamari', + }, + { + key: 'BD:BD-47', + label: 'Bangladesh — Noakhali', + }, + { + key: 'BD:BD-49', + label: 'Bangladesh — Pabna', + }, + { + key: 'BD:BD-52', + label: 'Bangladesh — Panchagarh', + }, + { + key: 'BD:BD-51', + label: 'Bangladesh — Patuakhali', + }, + { + key: 'BD:BD-50', + label: 'Bangladesh — Pirojpur', + }, + { + key: 'BD:BD-53', + label: 'Bangladesh — Rajbari', + }, + { + key: 'BD:BD-54', + label: 'Bangladesh — Rajshahi', + }, + { + key: 'BD:BD-56', + label: 'Bangladesh — Rangamati', + }, + { + key: 'BD:BD-55', + label: 'Bangladesh — Rangpur', + }, + { + key: 'BD:BD-58', + label: 'Bangladesh — Satkhira', + }, + { + key: 'BD:BD-62', + label: 'Bangladesh — Shariatpur', + }, + { + key: 'BD:BD-57', + label: 'Bangladesh — Sherpur', + }, + { + key: 'BD:BD-59', + label: 'Bangladesh — Sirajganj', + }, + { + key: 'BD:BD-61', + label: 'Bangladesh — Sunamganj', + }, + { + key: 'BD:BD-60', + label: 'Bangladesh — Sylhet', + }, + { + key: 'BD:BD-63', + label: 'Bangladesh — Tangail', + }, + { + key: 'BD:BD-64', + label: 'Bangladesh — Thakurgaon', + }, + { + key: 'BB', + label: 'Barbados', + }, + { + key: 'BY', + label: 'Belarus', + }, + { + key: 'PW', + label: 'Belau', + }, + { + key: 'BE', + label: 'Belgium', + }, + { + key: 'BZ', + label: 'Belize', + }, + { + key: 'BJ:AL', + label: 'Benin — Alibori', + }, + { + key: 'BJ:AK', + label: 'Benin — Atakora', + }, + { + key: 'BJ:AQ', + label: 'Benin — Atlantique', + }, + { + key: 'BJ:BO', + label: 'Benin — Borgou', + }, + { + key: 'BJ:CO', + label: 'Benin — Collines', + }, + { + key: 'BJ:KO', + label: 'Benin — Kouffo', + }, + { + key: 'BJ:DO', + label: 'Benin — Donga', + }, + { + key: 'BJ:LI', + label: 'Benin — Littoral', + }, + { + key: 'BJ:MO', + label: 'Benin — Mono', + }, + { + key: 'BJ:OU', + label: 'Benin — Ouémé', + }, + { + key: 'BJ:PL', + label: 'Benin — Plateau', + }, + { + key: 'BJ:ZO', + label: 'Benin — Zou', + }, + { + key: 'BM', + label: 'Bermuda', + }, + { + key: 'BT', + label: 'Bhutan', + }, + { + key: 'BO:BO-B', + label: 'Bolivia — Beni', + }, + { + key: 'BO:BO-H', + label: 'Bolivia — Chuquisaca', + }, + { + key: 'BO:BO-C', + label: 'Bolivia — Cochabamba', + }, + { + key: 'BO:BO-L', + label: 'Bolivia — La Paz', + }, + { + key: 'BO:BO-O', + label: 'Bolivia — Oruro', + }, + { + key: 'BO:BO-N', + label: 'Bolivia — Pando', + }, + { + key: 'BO:BO-P', + label: 'Bolivia — Potosí', + }, + { + key: 'BO:BO-S', + label: 'Bolivia — Santa Cruz', + }, + { + key: 'BO:BO-T', + label: 'Bolivia — Tarija', + }, + { + key: 'BQ', + label: 'Bonaire, Saint Eustatius and Saba', + }, + { + key: 'BA', + label: 'Bosnia and Herzegovina', + }, + { + key: 'BW', + label: 'Botswana', + }, + { + key: 'BV', + label: 'Bouvet Island', + }, + { + key: 'BR:AC', + label: 'Brazil — Acre', + }, + { + key: 'BR:AL', + label: 'Brazil — Alagoas', + }, + { + key: 'BR:AP', + label: 'Brazil — Amapá', + }, + { + key: 'BR:AM', + label: 'Brazil — Amazonas', + }, + { + key: 'BR:BA', + label: 'Brazil — Bahia', + }, + { + key: 'BR:CE', + label: 'Brazil — Ceará', + }, + { + key: 'BR:DF', + label: 'Brazil — Distrito Federal', + }, + { + key: 'BR:ES', + label: 'Brazil — Espírito Santo', + }, + { + key: 'BR:GO', + label: 'Brazil — Goiás', + }, + { + key: 'BR:MA', + label: 'Brazil — Maranhão', + }, + { + key: 'BR:MT', + label: 'Brazil — Mato Grosso', + }, + { + key: 'BR:MS', + label: 'Brazil — Mato Grosso do Sul', + }, + { + key: 'BR:MG', + label: 'Brazil — Minas Gerais', + }, + { + key: 'BR:PA', + label: 'Brazil — Pará', + }, + { + key: 'BR:PB', + label: 'Brazil — Paraíba', + }, + { + key: 'BR:PR', + label: 'Brazil — Paraná', + }, + { + key: 'BR:PE', + label: 'Brazil — Pernambuco', + }, + { + key: 'BR:PI', + label: 'Brazil — Piauí', + }, + { + key: 'BR:RJ', + label: 'Brazil — Rio de Janeiro', + }, + { + key: 'BR:RN', + label: 'Brazil — Rio Grande do Norte', + }, + { + key: 'BR:RS', + label: 'Brazil — Rio Grande do Sul', + }, + { + key: 'BR:RO', + label: 'Brazil — Rondônia', + }, + { + key: 'BR:RR', + label: 'Brazil — Roraima', + }, + { + key: 'BR:SC', + label: 'Brazil — Santa Catarina', + }, + { + key: 'BR:SP', + label: 'Brazil — São Paulo', + }, + { + key: 'BR:SE', + label: 'Brazil — Sergipe', + }, + { + key: 'BR:TO', + label: 'Brazil — Tocantins', + }, + { + key: 'IO', + label: 'British Indian Ocean Territory', + }, + { + key: 'BN', + label: 'Brunei', + }, + { + key: 'BG:BG-01', + label: 'Bulgaria — Blagoevgrad', + }, + { + key: 'BG:BG-02', + label: 'Bulgaria — Burgas', + }, + { + key: 'BG:BG-08', + label: 'Bulgaria — Dobrich', + }, + { + key: 'BG:BG-07', + label: 'Bulgaria — Gabrovo', + }, + { + key: 'BG:BG-26', + label: 'Bulgaria — Haskovo', + }, + { + key: 'BG:BG-09', + label: 'Bulgaria — Kardzhali', + }, + { + key: 'BG:BG-10', + label: 'Bulgaria — Kyustendil', + }, + { + key: 'BG:BG-11', + label: 'Bulgaria — Lovech', + }, + { + key: 'BG:BG-12', + label: 'Bulgaria — Montana', + }, + { + key: 'BG:BG-13', + label: 'Bulgaria — Pazardzhik', + }, + { + key: 'BG:BG-14', + label: 'Bulgaria — Pernik', + }, + { + key: 'BG:BG-15', + label: 'Bulgaria — Pleven', + }, + { + key: 'BG:BG-16', + label: 'Bulgaria — Plovdiv', + }, + { + key: 'BG:BG-17', + label: 'Bulgaria — Razgrad', + }, + { + key: 'BG:BG-18', + label: 'Bulgaria — Ruse', + }, + { + key: 'BG:BG-27', + label: 'Bulgaria — Shumen', + }, + { + key: 'BG:BG-19', + label: 'Bulgaria — Silistra', + }, + { + key: 'BG:BG-20', + label: 'Bulgaria — Sliven', + }, + { + key: 'BG:BG-21', + label: 'Bulgaria — Smolyan', + }, + { + key: 'BG:BG-23', + label: 'Bulgaria — Sofia District', + }, + { + key: 'BG:BG-22', + label: 'Bulgaria — Sofia', + }, + { + key: 'BG:BG-24', + label: 'Bulgaria — Stara Zagora', + }, + { + key: 'BG:BG-25', + label: 'Bulgaria — Targovishte', + }, + { + key: 'BG:BG-03', + label: 'Bulgaria — Varna', + }, + { + key: 'BG:BG-04', + label: 'Bulgaria — Veliko Tarnovo', + }, + { + key: 'BG:BG-05', + label: 'Bulgaria — Vidin', + }, + { + key: 'BG:BG-06', + label: 'Bulgaria — Vratsa', + }, + { + key: 'BG:BG-28', + label: 'Bulgaria — Yambol', + }, + { + key: 'BF', + label: 'Burkina Faso', + }, + { + key: 'BI', + label: 'Burundi', + }, + { + key: 'KH', + label: 'Cambodia', + }, + { + key: 'CM', + label: 'Cameroon', + }, + { + key: 'CA:AB', + label: 'Canada — Alberta', + }, + { + key: 'CA:BC', + label: 'Canada — British Columbia', + }, + { + key: 'CA:MB', + label: 'Canada — Manitoba', + }, + { + key: 'CA:NB', + label: 'Canada — New Brunswick', + }, + { + key: 'CA:NL', + label: 'Canada — Newfoundland and Labrador', + }, + { + key: 'CA:NT', + label: 'Canada — Northwest Territories', + }, + { + key: 'CA:NS', + label: 'Canada — Nova Scotia', + }, + { + key: 'CA:NU', + label: 'Canada — Nunavut', + }, + { + key: 'CA:ON', + label: 'Canada — Ontario', + }, + { + key: 'CA:PE', + label: 'Canada — Prince Edward Island', + }, + { + key: 'CA:QC', + label: 'Canada — Quebec', + }, + { + key: 'CA:SK', + label: 'Canada — Saskatchewan', + }, + { + key: 'CA:YT', + label: 'Canada — Yukon Territory', + }, + { + key: 'CV', + label: 'Cape Verde', + }, + { + key: 'KY', + label: 'Cayman Islands', + }, + { + key: 'CF', + label: 'Central African Republic', + }, + { + key: 'TD', + label: 'Chad', + }, + { + key: 'CL:CL-AI', + label: 'Chile — Aisén del General Carlos Ibañez del Campo', + }, + { + key: 'CL:CL-AN', + label: 'Chile — Antofagasta', + }, + { + key: 'CL:CL-AP', + label: 'Chile — Arica y Parinacota', + }, + { + key: 'CL:CL-AR', + label: 'Chile — La Araucanía', + }, + { + key: 'CL:CL-AT', + label: 'Chile — Atacama', + }, + { + key: 'CL:CL-BI', + label: 'Chile — Biobío', + }, + { + key: 'CL:CL-CO', + label: 'Chile — Coquimbo', + }, + { + key: 'CL:CL-LI', + label: "Chile — Libertador General Bernardo O'Higgins", + }, + { + key: 'CL:CL-LL', + label: 'Chile — Los Lagos', + }, + { + key: 'CL:CL-LR', + label: 'Chile — Los Ríos', + }, + { + key: 'CL:CL-MA', + label: 'Chile — Magallanes', + }, + { + key: 'CL:CL-ML', + label: 'Chile — Maule', + }, + { + key: 'CL:CL-NB', + label: 'Chile — Ñuble', + }, + { + key: 'CL:CL-RM', + label: 'Chile — Región Metropolitana de Santiago', + }, + { + key: 'CL:CL-TA', + label: 'Chile — Tarapacá', + }, + { + key: 'CL:CL-VS', + label: 'Chile — Valparaíso', + }, + { + key: 'CN:CN1', + label: 'China — Yunnan / 云南', + }, + { + key: 'CN:CN2', + label: 'China — Beijing / 北京', + }, + { + key: 'CN:CN3', + label: 'China — Tianjin / 天津', + }, + { + key: 'CN:CN4', + label: 'China — Hebei / 河北', + }, + { + key: 'CN:CN5', + label: 'China — Shanxi / 山西', + }, + { + key: 'CN:CN6', + label: 'China — Inner Mongolia / 內蒙古', + }, + { + key: 'CN:CN7', + label: 'China — Liaoning / 辽宁', + }, + { + key: 'CN:CN8', + label: 'China — Jilin / 吉林', + }, + { + key: 'CN:CN9', + label: 'China — Heilongjiang / 黑龙江', + }, + { + key: 'CN:CN10', + label: 'China — Shanghai / 上海', + }, + { + key: 'CN:CN11', + label: 'China — Jiangsu / 江苏', + }, + { + key: 'CN:CN12', + label: 'China — Zhejiang / 浙江', + }, + { + key: 'CN:CN13', + label: 'China — Anhui / 安徽', + }, + { + key: 'CN:CN14', + label: 'China — Fujian / 福建', + }, + { + key: 'CN:CN15', + label: 'China — Jiangxi / 江西', + }, + { + key: 'CN:CN16', + label: 'China — Shandong / 山东', + }, + { + key: 'CN:CN17', + label: 'China — Henan / 河南', + }, + { + key: 'CN:CN18', + label: 'China — Hubei / 湖北', + }, + { + key: 'CN:CN19', + label: 'China — Hunan / 湖南', + }, + { + key: 'CN:CN20', + label: 'China — Guangdong / 广东', + }, + { + key: 'CN:CN21', + label: 'China — Guangxi Zhuang / 广西壮族', + }, + { + key: 'CN:CN22', + label: 'China — Hainan / 海南', + }, + { + key: 'CN:CN23', + label: 'China — Chongqing / 重庆', + }, + { + key: 'CN:CN24', + label: 'China — Sichuan / 四川', + }, + { + key: 'CN:CN25', + label: 'China — Guizhou / 贵州', + }, + { + key: 'CN:CN26', + label: 'China — Shaanxi / 陕西', + }, + { + key: 'CN:CN27', + label: 'China — Gansu / 甘肃', + }, + { + key: 'CN:CN28', + label: 'China — Qinghai / 青海', + }, + { + key: 'CN:CN29', + label: 'China — Ningxia Hui / 宁夏', + }, + { + key: 'CN:CN30', + label: 'China — Macao / 澳门', + }, + { + key: 'CN:CN31', + label: 'China — Tibet / 西藏', + }, + { + key: 'CN:CN32', + label: 'China — Xinjiang / 新疆', + }, + { + key: 'CX', + label: 'Christmas Island', + }, + { + key: 'CC', + label: 'Cocos (Keeling) Islands', + }, + { + key: 'CO:CO-AMA', + label: 'Colombia — Amazonas', + }, + { + key: 'CO:CO-ANT', + label: 'Colombia — Antioquia', + }, + { + key: 'CO:CO-ARA', + label: 'Colombia — Arauca', + }, + { + key: 'CO:CO-ATL', + label: 'Colombia — Atlántico', + }, + { + key: 'CO:CO-BOL', + label: 'Colombia — Bolívar', + }, + { + key: 'CO:CO-BOY', + label: 'Colombia — Boyacá', + }, + { + key: 'CO:CO-CAL', + label: 'Colombia — Caldas', + }, + { + key: 'CO:CO-CAQ', + label: 'Colombia — Caquetá', + }, + { + key: 'CO:CO-CAS', + label: 'Colombia — Casanare', + }, + { + key: 'CO:CO-CAU', + label: 'Colombia — Cauca', + }, + { + key: 'CO:CO-CES', + label: 'Colombia — Cesar', + }, + { + key: 'CO:CO-CHO', + label: 'Colombia — Chocó', + }, + { + key: 'CO:CO-COR', + label: 'Colombia — Córdoba', + }, + { + key: 'CO:CO-CUN', + label: 'Colombia — Cundinamarca', + }, + { + key: 'CO:CO-DC', + label: 'Colombia — Capital District', + }, + { + key: 'CO:CO-GUA', + label: 'Colombia — Guainía', + }, + { + key: 'CO:CO-GUV', + label: 'Colombia — Guaviare', + }, + { + key: 'CO:CO-HUI', + label: 'Colombia — Huila', + }, + { + key: 'CO:CO-LAG', + label: 'Colombia — La Guajira', + }, + { + key: 'CO:CO-MAG', + label: 'Colombia — Magdalena', + }, + { + key: 'CO:CO-MET', + label: 'Colombia — Meta', + }, + { + key: 'CO:CO-NAR', + label: 'Colombia — Nariño', + }, + { + key: 'CO:CO-NSA', + label: 'Colombia — Norte de Santander', + }, + { + key: 'CO:CO-PUT', + label: 'Colombia — Putumayo', + }, + { + key: 'CO:CO-QUI', + label: 'Colombia — Quindío', + }, + { + key: 'CO:CO-RIS', + label: 'Colombia — Risaralda', + }, + { + key: 'CO:CO-SAN', + label: 'Colombia — Santander', + }, + { + key: 'CO:CO-SAP', + label: 'Colombia — San Andrés & Providencia', + }, + { + key: 'CO:CO-SUC', + label: 'Colombia — Sucre', + }, + { + key: 'CO:CO-TOL', + label: 'Colombia — Tolima', + }, + { + key: 'CO:CO-VAC', + label: 'Colombia — Valle del Cauca', + }, + { + key: 'CO:CO-VAU', + label: 'Colombia — Vaupés', + }, + { + key: 'CO:CO-VID', + label: 'Colombia — Vichada', + }, + { + key: 'KM', + label: 'Comoros', + }, + { + key: 'CG', + label: 'Congo (Brazzaville)', + }, + { + key: 'CD', + label: 'Congo (Kinshasa)', + }, + { + key: 'CK', + label: 'Cook Islands', + }, + { + key: 'CR:CR-A', + label: 'Costa Rica — Alajuela', + }, + { + key: 'CR:CR-C', + label: 'Costa Rica — Cartago', + }, + { + key: 'CR:CR-G', + label: 'Costa Rica — Guanacaste', + }, + { + key: 'CR:CR-H', + label: 'Costa Rica — Heredia', + }, + { + key: 'CR:CR-L', + label: 'Costa Rica — Limón', + }, + { + key: 'CR:CR-P', + label: 'Costa Rica — Puntarenas', + }, + { + key: 'CR:CR-SJ', + label: 'Costa Rica — San José', + }, + { + key: 'HR', + label: 'Croatia', + }, + { + key: 'CU', + label: 'Cuba', + }, + { + key: 'CW', + label: 'Curaçao', + }, + { + key: 'CY', + label: 'Cyprus', + }, + { + key: 'CZ', + label: 'Czech Republic', + }, + { + key: 'DK', + label: 'Denmark', + }, + { + key: 'DJ', + label: 'Djibouti', + }, + { + key: 'DM', + label: 'Dominica', + }, + { + key: 'DO:DO-01', + label: 'Dominican Republic — Distrito Nacional', + }, + { + key: 'DO:DO-02', + label: 'Dominican Republic — Azua', + }, + { + key: 'DO:DO-03', + label: 'Dominican Republic — Baoruco', + }, + { + key: 'DO:DO-04', + label: 'Dominican Republic — Barahona', + }, + { + key: 'DO:DO-33', + label: 'Dominican Republic — Cibao Nordeste', + }, + { + key: 'DO:DO-34', + label: 'Dominican Republic — Cibao Noroeste', + }, + { + key: 'DO:DO-35', + label: 'Dominican Republic — Cibao Norte', + }, + { + key: 'DO:DO-36', + label: 'Dominican Republic — Cibao Sur', + }, + { + key: 'DO:DO-05', + label: 'Dominican Republic — Dajabón', + }, + { + key: 'DO:DO-06', + label: 'Dominican Republic — Duarte', + }, + { + key: 'DO:DO-08', + label: 'Dominican Republic — El Seibo', + }, + { + key: 'DO:DO-37', + label: 'Dominican Republic — El Valle', + }, + { + key: 'DO:DO-07', + label: 'Dominican Republic — Elías Piña', + }, + { + key: 'DO:DO-38', + label: 'Dominican Republic — Enriquillo', + }, + { + key: 'DO:DO-09', + label: 'Dominican Republic — Espaillat', + }, + { + key: 'DO:DO-30', + label: 'Dominican Republic — Hato Mayor', + }, + { + key: 'DO:DO-19', + label: 'Dominican Republic — Hermanas Mirabal', + }, + { + key: 'DO:DO-39', + label: 'Dominican Republic — Higüamo', + }, + { + key: 'DO:DO-10', + label: 'Dominican Republic — Independencia', + }, + { + key: 'DO:DO-11', + label: 'Dominican Republic — La Altagracia', + }, + { + key: 'DO:DO-12', + label: 'Dominican Republic — La Romana', + }, + { + key: 'DO:DO-13', + label: 'Dominican Republic — La Vega', + }, + { + key: 'DO:DO-14', + label: 'Dominican Republic — María Trinidad Sánchez', + }, + { + key: 'DO:DO-28', + label: 'Dominican Republic — Monseñor Nouel', + }, + { + key: 'DO:DO-15', + label: 'Dominican Republic — Monte Cristi', + }, + { + key: 'DO:DO-29', + label: 'Dominican Republic — Monte Plata', + }, + { + key: 'DO:DO-40', + label: 'Dominican Republic — Ozama', + }, + { + key: 'DO:DO-16', + label: 'Dominican Republic — Pedernales', + }, + { + key: 'DO:DO-17', + label: 'Dominican Republic — Peravia', + }, + { + key: 'DO:DO-18', + label: 'Dominican Republic — Puerto Plata', + }, + { + key: 'DO:DO-20', + label: 'Dominican Republic — Samaná', + }, + { + key: 'DO:DO-21', + label: 'Dominican Republic — San Cristóbal', + }, + { + key: 'DO:DO-31', + label: 'Dominican Republic — San José de Ocoa', + }, + { + key: 'DO:DO-22', + label: 'Dominican Republic — San Juan', + }, + { + key: 'DO:DO-23', + label: 'Dominican Republic — San Pedro de Macorís', + }, + { + key: 'DO:DO-24', + label: 'Dominican Republic — Sánchez Ramírez', + }, + { + key: 'DO:DO-25', + label: 'Dominican Republic — Santiago', + }, + { + key: 'DO:DO-26', + label: 'Dominican Republic — Santiago Rodríguez', + }, + { + key: 'DO:DO-32', + label: 'Dominican Republic — Santo Domingo', + }, + { + key: 'DO:DO-41', + label: 'Dominican Republic — Valdesia', + }, + { + key: 'DO:DO-27', + label: 'Dominican Republic — Valverde', + }, + { + key: 'DO:DO-42', + label: 'Dominican Republic — Yuma', + }, + { + key: 'EC:EC-A', + label: 'Ecuador — Azuay', + }, + { + key: 'EC:EC-B', + label: 'Ecuador — Bolívar', + }, + { + key: 'EC:EC-F', + label: 'Ecuador — Cañar', + }, + { + key: 'EC:EC-C', + label: 'Ecuador — Carchi', + }, + { + key: 'EC:EC-H', + label: 'Ecuador — Chimborazo', + }, + { + key: 'EC:EC-X', + label: 'Ecuador — Cotopaxi', + }, + { + key: 'EC:EC-O', + label: 'Ecuador — El Oro', + }, + { + key: 'EC:EC-E', + label: 'Ecuador — Esmeraldas', + }, + { + key: 'EC:EC-W', + label: 'Ecuador — Galápagos', + }, + { + key: 'EC:EC-G', + label: 'Ecuador — Guayas', + }, + { + key: 'EC:EC-I', + label: 'Ecuador — Imbabura', + }, + { + key: 'EC:EC-L', + label: 'Ecuador — Loja', + }, + { + key: 'EC:EC-R', + label: 'Ecuador — Los Ríos', + }, + { + key: 'EC:EC-M', + label: 'Ecuador — Manabí', + }, + { + key: 'EC:EC-S', + label: 'Ecuador — Morona-Santiago', + }, + { + key: 'EC:EC-N', + label: 'Ecuador — Napo', + }, + { + key: 'EC:EC-D', + label: 'Ecuador — Orellana', + }, + { + key: 'EC:EC-Y', + label: 'Ecuador — Pastaza', + }, + { + key: 'EC:EC-P', + label: 'Ecuador — Pichincha', + }, + { + key: 'EC:EC-SE', + label: 'Ecuador — Santa Elena', + }, + { + key: 'EC:EC-SD', + label: 'Ecuador — Santo Domingo de los Tsáchilas', + }, + { + key: 'EC:EC-U', + label: 'Ecuador — Sucumbíos', + }, + { + key: 'EC:EC-T', + label: 'Ecuador — Tungurahua', + }, + { + key: 'EC:EC-Z', + label: 'Ecuador — Zamora-Chinchipe', + }, + { + key: 'EG:EGALX', + label: 'Egypt — Alexandria', + }, + { + key: 'EG:EGASN', + label: 'Egypt — Aswan', + }, + { + key: 'EG:EGAST', + label: 'Egypt — Asyut', + }, + { + key: 'EG:EGBA', + label: 'Egypt — Red Sea', + }, + { + key: 'EG:EGBH', + label: 'Egypt — Beheira', + }, + { + key: 'EG:EGBNS', + label: 'Egypt — Beni Suef', + }, + { + key: 'EG:EGC', + label: 'Egypt — Cairo', + }, + { + key: 'EG:EGDK', + label: 'Egypt — Dakahlia', + }, + { + key: 'EG:EGDT', + label: 'Egypt — Damietta', + }, + { + key: 'EG:EGFYM', + label: 'Egypt — Faiyum', + }, + { + key: 'EG:EGGH', + label: 'Egypt — Gharbia', + }, + { + key: 'EG:EGGZ', + label: 'Egypt — Giza', + }, + { + key: 'EG:EGIS', + label: 'Egypt — Ismailia', + }, + { + key: 'EG:EGJS', + label: 'Egypt — South Sinai', + }, + { + key: 'EG:EGKB', + label: 'Egypt — Qalyubia', + }, + { + key: 'EG:EGKFS', + label: 'Egypt — Kafr el-Sheikh', + }, + { + key: 'EG:EGKN', + label: 'Egypt — Qena', + }, + { + key: 'EG:EGLX', + label: 'Egypt — Luxor', + }, + { + key: 'EG:EGMN', + label: 'Egypt — Minya', + }, + { + key: 'EG:EGMNF', + label: 'Egypt — Monufia', + }, + { + key: 'EG:EGMT', + label: 'Egypt — Matrouh', + }, + { + key: 'EG:EGPTS', + label: 'Egypt — Port Said', + }, + { + key: 'EG:EGSHG', + label: 'Egypt — Sohag', + }, + { + key: 'EG:EGSHR', + label: 'Egypt — Al Sharqia', + }, + { + key: 'EG:EGSIN', + label: 'Egypt — North Sinai', + }, + { + key: 'EG:EGSUZ', + label: 'Egypt — Suez', + }, + { + key: 'EG:EGWAD', + label: 'Egypt — New Valley', + }, + { + key: 'SV:SV-AH', + label: 'El Salvador — Ahuachapán', + }, + { + key: 'SV:SV-CA', + label: 'El Salvador — Cabañas', + }, + { + key: 'SV:SV-CH', + label: 'El Salvador — Chalatenango', + }, + { + key: 'SV:SV-CU', + label: 'El Salvador — Cuscatlán', + }, + { + key: 'SV:SV-LI', + label: 'El Salvador — La Libertad', + }, + { + key: 'SV:SV-MO', + label: 'El Salvador — Morazán', + }, + { + key: 'SV:SV-PA', + label: 'El Salvador — La Paz', + }, + { + key: 'SV:SV-SA', + label: 'El Salvador — Santa Ana', + }, + { + key: 'SV:SV-SM', + label: 'El Salvador — San Miguel', + }, + { + key: 'SV:SV-SO', + label: 'El Salvador — Sonsonate', + }, + { + key: 'SV:SV-SS', + label: 'El Salvador — San Salvador', + }, + { + key: 'SV:SV-SV', + label: 'El Salvador — San Vicente', + }, + { + key: 'SV:SV-UN', + label: 'El Salvador — La Unión', + }, + { + key: 'SV:SV-US', + label: 'El Salvador — Usulután', + }, + { + key: 'GQ', + label: 'Equatorial Guinea', + }, + { + key: 'ER', + label: 'Eritrea', + }, + { + key: 'EE', + label: 'Estonia', + }, + { + key: 'SZ', + label: 'Eswatini', + }, + { + key: 'ET', + label: 'Ethiopia', + }, + { + key: 'FK', + label: 'Falkland Islands', + }, + { + key: 'FO', + label: 'Faroe Islands', + }, + { + key: 'FJ', + label: 'Fiji', + }, + { + key: 'FI', + label: 'Finland', + }, + { + key: 'FR', + label: 'France', + }, + { + key: 'GF', + label: 'French Guiana', + }, + { + key: 'PF', + label: 'French Polynesia', + }, + { + key: 'TF', + label: 'French Southern Territories', + }, + { + key: 'GA', + label: 'Gabon', + }, + { + key: 'GM', + label: 'Gambia', + }, + { + key: 'GE', + label: 'Georgia', + }, + { + key: 'DE:DE-BW', + label: 'Germany — Baden-Württemberg', + }, + { + key: 'DE:DE-BY', + label: 'Germany — Bavaria', + }, + { + key: 'DE:DE-BE', + label: 'Germany — Berlin', + }, + { + key: 'DE:DE-BB', + label: 'Germany — Brandenburg', + }, + { + key: 'DE:DE-HB', + label: 'Germany — Bremen', + }, + { + key: 'DE:DE-HH', + label: 'Germany — Hamburg', + }, + { + key: 'DE:DE-HE', + label: 'Germany — Hesse', + }, + { + key: 'DE:DE-MV', + label: 'Germany — Mecklenburg-Vorpommern', + }, + { + key: 'DE:DE-NI', + label: 'Germany — Lower Saxony', + }, + { + key: 'DE:DE-NW', + label: 'Germany — North Rhine-Westphalia', + }, + { + key: 'DE:DE-RP', + label: 'Germany — Rhineland-Palatinate', + }, + { + key: 'DE:DE-SL', + label: 'Germany — Saarland', + }, + { + key: 'DE:DE-SN', + label: 'Germany — Saxony', + }, + { + key: 'DE:DE-ST', + label: 'Germany — Saxony-Anhalt', + }, + { + key: 'DE:DE-SH', + label: 'Germany — Schleswig-Holstein', + }, + { + key: 'DE:DE-TH', + label: 'Germany — Thuringia', + }, + { + key: 'GH:AF', + label: 'Ghana — Ahafo', + }, + { + key: 'GH:AH', + label: 'Ghana — Ashanti', + }, + { + key: 'GH:BA', + label: 'Ghana — Brong-Ahafo', + }, + { + key: 'GH:BO', + label: 'Ghana — Bono', + }, + { + key: 'GH:BE', + label: 'Ghana — Bono East', + }, + { + key: 'GH:CP', + label: 'Ghana — Central', + }, + { + key: 'GH:EP', + label: 'Ghana — Eastern', + }, + { + key: 'GH:AA', + label: 'Ghana — Greater Accra', + }, + { + key: 'GH:NE', + label: 'Ghana — North East', + }, + { + key: 'GH:NP', + label: 'Ghana — Northern', + }, + { + key: 'GH:OT', + label: 'Ghana — Oti', + }, + { + key: 'GH:SV', + label: 'Ghana — Savannah', + }, + { + key: 'GH:UE', + label: 'Ghana — Upper East', + }, + { + key: 'GH:UW', + label: 'Ghana — Upper West', + }, + { + key: 'GH:TV', + label: 'Ghana — Volta', + }, + { + key: 'GH:WP', + label: 'Ghana — Western', + }, + { + key: 'GH:WN', + label: 'Ghana — Western North', + }, + { + key: 'GI', + label: 'Gibraltar', + }, + { + key: 'GR:I', + label: 'Greece — Attica', + }, + { + key: 'GR:A', + label: 'Greece — East Macedonia and Thrace', + }, + { + key: 'GR:B', + label: 'Greece — Central Macedonia', + }, + { + key: 'GR:C', + label: 'Greece — West Macedonia', + }, + { + key: 'GR:D', + label: 'Greece — Epirus', + }, + { + key: 'GR:E', + label: 'Greece — Thessaly', + }, + { + key: 'GR:F', + label: 'Greece — Ionian Islands', + }, + { + key: 'GR:G', + label: 'Greece — West Greece', + }, + { + key: 'GR:H', + label: 'Greece — Central Greece', + }, + { + key: 'GR:J', + label: 'Greece — Peloponnese', + }, + { + key: 'GR:K', + label: 'Greece — North Aegean', + }, + { + key: 'GR:L', + label: 'Greece — South Aegean', + }, + { + key: 'GR:M', + label: 'Greece — Crete', + }, + { + key: 'GL', + label: 'Greenland', + }, + { + key: 'GD', + label: 'Grenada', + }, + { + key: 'GP', + label: 'Guadeloupe', + }, + { + key: 'GU', + label: 'Guam', + }, + { + key: 'GT:GT-AV', + label: 'Guatemala — Alta Verapaz', + }, + { + key: 'GT:GT-BV', + label: 'Guatemala — Baja Verapaz', + }, + { + key: 'GT:GT-CM', + label: 'Guatemala — Chimaltenango', + }, + { + key: 'GT:GT-CQ', + label: 'Guatemala — Chiquimula', + }, + { + key: 'GT:GT-PR', + label: 'Guatemala — El Progreso', + }, + { + key: 'GT:GT-ES', + label: 'Guatemala — Escuintla', + }, + { + key: 'GT:GT-GU', + label: 'Guatemala — Guatemala', + }, + { + key: 'GT:GT-HU', + label: 'Guatemala — Huehuetenango', + }, + { + key: 'GT:GT-IZ', + label: 'Guatemala — Izabal', + }, + { + key: 'GT:GT-JA', + label: 'Guatemala — Jalapa', + }, + { + key: 'GT:GT-JU', + label: 'Guatemala — Jutiapa', + }, + { + key: 'GT:GT-PE', + label: 'Guatemala — Petén', + }, + { + key: 'GT:GT-QZ', + label: 'Guatemala — Quetzaltenango', + }, + { + key: 'GT:GT-QC', + label: 'Guatemala — Quiché', + }, + { + key: 'GT:GT-RE', + label: 'Guatemala — Retalhuleu', + }, + { + key: 'GT:GT-SA', + label: 'Guatemala — Sacatepéquez', + }, + { + key: 'GT:GT-SM', + label: 'Guatemala — San Marcos', + }, + { + key: 'GT:GT-SR', + label: 'Guatemala — Santa Rosa', + }, + { + key: 'GT:GT-SO', + label: 'Guatemala — Sololá', + }, + { + key: 'GT:GT-SU', + label: 'Guatemala — Suchitepéquez', + }, + { + key: 'GT:GT-TO', + label: 'Guatemala — Totonicapán', + }, + { + key: 'GT:GT-ZA', + label: 'Guatemala — Zacapa', + }, + { + key: 'GG', + label: 'Guernsey', + }, + { + key: 'GN', + label: 'Guinea', + }, + { + key: 'GW', + label: 'Guinea-Bissau', + }, + { + key: 'GY', + label: 'Guyana', + }, + { + key: 'HT', + label: 'Haiti', + }, + { + key: 'HM', + label: 'Heard Island and McDonald Islands', + }, + { + key: 'HN:HN-AT', + label: 'Honduras — Atlántida', + }, + { + key: 'HN:HN-IB', + label: 'Honduras — Bay Islands', + }, + { + key: 'HN:HN-CH', + label: 'Honduras — Choluteca', + }, + { + key: 'HN:HN-CL', + label: 'Honduras — Colón', + }, + { + key: 'HN:HN-CM', + label: 'Honduras — Comayagua', + }, + { + key: 'HN:HN-CP', + label: 'Honduras — Copán', + }, + { + key: 'HN:HN-CR', + label: 'Honduras — Cortés', + }, + { + key: 'HN:HN-EP', + label: 'Honduras — El Paraíso', + }, + { + key: 'HN:HN-FM', + label: 'Honduras — Francisco Morazán', + }, + { + key: 'HN:HN-GD', + label: 'Honduras — Gracias a Dios', + }, + { + key: 'HN:HN-IN', + label: 'Honduras — Intibucá', + }, + { + key: 'HN:HN-LE', + label: 'Honduras — Lempira', + }, + { + key: 'HN:HN-LP', + label: 'Honduras — La Paz', + }, + { + key: 'HN:HN-OC', + label: 'Honduras — Ocotepeque', + }, + { + key: 'HN:HN-OL', + label: 'Honduras — Olancho', + }, + { + key: 'HN:HN-SB', + label: 'Honduras — Santa Bárbara', + }, + { + key: 'HN:HN-VA', + label: 'Honduras — Valle', + }, + { + key: 'HN:HN-YO', + label: 'Honduras — Yoro', + }, + { + key: 'HK:HONG KONG', + label: 'Hong Kong — Hong Kong Island', + }, + { + key: 'HK:KOWLOON', + label: 'Hong Kong — Kowloon', + }, + { + key: 'HK:NEW TERRITORIES', + label: 'Hong Kong — New Territories', + }, + { + key: 'HU:BK', + label: 'Hungary — Bács-Kiskun', + }, + { + key: 'HU:BE', + label: 'Hungary — Békés', + }, + { + key: 'HU:BA', + label: 'Hungary — Baranya', + }, + { + key: 'HU:BZ', + label: 'Hungary — Borsod-Abaúj-Zemplén', + }, + { + key: 'HU:BU', + label: 'Hungary — Budapest', + }, + { + key: 'HU:CS', + label: 'Hungary — Csongrád-Csanád', + }, + { + key: 'HU:FE', + label: 'Hungary — Fejér', + }, + { + key: 'HU:GS', + label: 'Hungary — Győr-Moson-Sopron', + }, + { + key: 'HU:HB', + label: 'Hungary — Hajdú-Bihar', + }, + { + key: 'HU:HE', + label: 'Hungary — Heves', + }, + { + key: 'HU:JN', + label: 'Hungary — Jász-Nagykun-Szolnok', + }, + { + key: 'HU:KE', + label: 'Hungary — Komárom-Esztergom', + }, + { + key: 'HU:NO', + label: 'Hungary — Nógrád', + }, + { + key: 'HU:PE', + label: 'Hungary — Pest', + }, + { + key: 'HU:SO', + label: 'Hungary — Somogy', + }, + { + key: 'HU:SZ', + label: 'Hungary — Szabolcs-Szatmár-Bereg', + }, + { + key: 'HU:TO', + label: 'Hungary — Tolna', + }, + { + key: 'HU:VA', + label: 'Hungary — Vas', + }, + { + key: 'HU:VE', + label: 'Hungary — Veszprém', + }, + { + key: 'HU:ZA', + label: 'Hungary — Zala', + }, + { + key: 'IS', + label: 'Iceland', + }, + { + key: 'IN:AP', + label: 'India — Andhra Pradesh', + }, + { + key: 'IN:AR', + label: 'India — Arunachal Pradesh', + }, + { + key: 'IN:AS', + label: 'India — Assam', + }, + { + key: 'IN:BR', + label: 'India — Bihar', + }, + { + key: 'IN:CT', + label: 'India — Chhattisgarh', + }, + { + key: 'IN:GA', + label: 'India — Goa', + }, + { + key: 'IN:GJ', + label: 'India — Gujarat', + }, + { + key: 'IN:HR', + label: 'India — Haryana', + }, + { + key: 'IN:HP', + label: 'India — Himachal Pradesh', + }, + { + key: 'IN:JK', + label: 'India — Jammu and Kashmir', + }, + { + key: 'IN:JH', + label: 'India — Jharkhand', + }, + { + key: 'IN:KA', + label: 'India — Karnataka', + }, + { + key: 'IN:KL', + label: 'India — Kerala', + }, + { + key: 'IN:LA', + label: 'India — Ladakh', + }, + { + key: 'IN:MP', + label: 'India — Madhya Pradesh', + }, + { + key: 'IN:MH', + label: 'India — Maharashtra', + }, + { + key: 'IN:MN', + label: 'India — Manipur', + }, + { + key: 'IN:ML', + label: 'India — Meghalaya', + }, + { + key: 'IN:MZ', + label: 'India — Mizoram', + }, + { + key: 'IN:NL', + label: 'India — Nagaland', + }, + { + key: 'IN:OR', + label: 'India — Odisha', + }, + { + key: 'IN:PB', + label: 'India — Punjab', + }, + { + key: 'IN:RJ', + label: 'India — Rajasthan', + }, + { + key: 'IN:SK', + label: 'India — Sikkim', + }, + { + key: 'IN:TN', + label: 'India — Tamil Nadu', + }, + { + key: 'IN:TS', + label: 'India — Telangana', + }, + { + key: 'IN:TR', + label: 'India — Tripura', + }, + { + key: 'IN:UK', + label: 'India — Uttarakhand', + }, + { + key: 'IN:UP', + label: 'India — Uttar Pradesh', + }, + { + key: 'IN:WB', + label: 'India — West Bengal', + }, + { + key: 'IN:AN', + label: 'India — Andaman and Nicobar Islands', + }, + { + key: 'IN:CH', + label: 'India — Chandigarh', + }, + { + key: 'IN:DN', + label: 'India — Dadra and Nagar Haveli', + }, + { + key: 'IN:DD', + label: 'India — Daman and Diu', + }, + { + key: 'IN:DL', + label: 'India — Delhi', + }, + { + key: 'IN:LD', + label: 'India — Lakshadeep', + }, + { + key: 'IN:PY', + label: 'India — Pondicherry (Puducherry)', + }, + { + key: 'ID:AC', + label: 'Indonesia — Daerah Istimewa Aceh', + }, + { + key: 'ID:SU', + label: 'Indonesia — Sumatera Utara', + }, + { + key: 'ID:SB', + label: 'Indonesia — Sumatera Barat', + }, + { + key: 'ID:RI', + label: 'Indonesia — Riau', + }, + { + key: 'ID:KR', + label: 'Indonesia — Kepulauan Riau', + }, + { + key: 'ID:JA', + label: 'Indonesia — Jambi', + }, + { + key: 'ID:SS', + label: 'Indonesia — Sumatera Selatan', + }, + { + key: 'ID:BB', + label: 'Indonesia — Bangka Belitung', + }, + { + key: 'ID:BE', + label: 'Indonesia — Bengkulu', + }, + { + key: 'ID:LA', + label: 'Indonesia — Lampung', + }, + { + key: 'ID:JK', + label: 'Indonesia — DKI Jakarta', + }, + { + key: 'ID:JB', + label: 'Indonesia — Jawa Barat', + }, + { + key: 'ID:BT', + label: 'Indonesia — Banten', + }, + { + key: 'ID:JT', + label: 'Indonesia — Jawa Tengah', + }, + { + key: 'ID:JI', + label: 'Indonesia — Jawa Timur', + }, + { + key: 'ID:YO', + label: 'Indonesia — Daerah Istimewa Yogyakarta', + }, + { + key: 'ID:BA', + label: 'Indonesia — Bali', + }, + { + key: 'ID:NB', + label: 'Indonesia — Nusa Tenggara Barat', + }, + { + key: 'ID:NT', + label: 'Indonesia — Nusa Tenggara Timur', + }, + { + key: 'ID:KB', + label: 'Indonesia — Kalimantan Barat', + }, + { + key: 'ID:KT', + label: 'Indonesia — Kalimantan Tengah', + }, + { + key: 'ID:KI', + label: 'Indonesia — Kalimantan Timur', + }, + { + key: 'ID:KS', + label: 'Indonesia — Kalimantan Selatan', + }, + { + key: 'ID:KU', + label: 'Indonesia — Kalimantan Utara', + }, + { + key: 'ID:SA', + label: 'Indonesia — Sulawesi Utara', + }, + { + key: 'ID:ST', + label: 'Indonesia — Sulawesi Tengah', + }, + { + key: 'ID:SG', + label: 'Indonesia — Sulawesi Tenggara', + }, + { + key: 'ID:SR', + label: 'Indonesia — Sulawesi Barat', + }, + { + key: 'ID:SN', + label: 'Indonesia — Sulawesi Selatan', + }, + { + key: 'ID:GO', + label: 'Indonesia — Gorontalo', + }, + { + key: 'ID:MA', + label: 'Indonesia — Maluku', + }, + { + key: 'ID:MU', + label: 'Indonesia — Maluku Utara', + }, + { + key: 'ID:PA', + label: 'Indonesia — Papua', + }, + { + key: 'ID:PB', + label: 'Indonesia — Papua Barat', + }, + { + key: 'IR:KHZ', + label: 'Iran — Khuzestan (خوزستان)', + }, + { + key: 'IR:THR', + label: 'Iran — Tehran (تهران)', + }, + { + key: 'IR:ILM', + label: 'Iran — Ilaam (ایلام)', + }, + { + key: 'IR:BHR', + label: 'Iran — Bushehr (بوشهر)', + }, + { + key: 'IR:ADL', + label: 'Iran — Ardabil (اردبیل)', + }, + { + key: 'IR:ESF', + label: 'Iran — Isfahan (اصفهان)', + }, + { + key: 'IR:YZD', + label: 'Iran — Yazd (یزد)', + }, + { + key: 'IR:KRH', + label: 'Iran — Kermanshah (کرمانشاه)', + }, + { + key: 'IR:KRN', + label: 'Iran — Kerman (کرمان)', + }, + { + key: 'IR:HDN', + label: 'Iran — Hamadan (همدان)', + }, + { + key: 'IR:GZN', + label: 'Iran — Ghazvin (قزوین)', + }, + { + key: 'IR:ZJN', + label: 'Iran — Zanjan (زنجان)', + }, + { + key: 'IR:LRS', + label: 'Iran — Luristan (لرستان)', + }, + { + key: 'IR:ABZ', + label: 'Iran — Alborz (البرز)', + }, + { + key: 'IR:EAZ', + label: 'Iran — East Azarbaijan (آذربایجان شرقی)', + }, + { + key: 'IR:WAZ', + label: 'Iran — West Azarbaijan (آذربایجان غربی)', + }, + { + key: 'IR:CHB', + label: 'Iran — Chaharmahal and Bakhtiari (چهارمحال و بختیاری)', + }, + { + key: 'IR:SKH', + label: 'Iran — South Khorasan (خراسان جنوبی)', + }, + { + key: 'IR:RKH', + label: 'Iran — Razavi Khorasan (خراسان رضوی)', + }, + { + key: 'IR:NKH', + label: 'Iran — North Khorasan (خراسان شمالی)', + }, + { + key: 'IR:SMN', + label: 'Iran — Semnan (سمنان)', + }, + { + key: 'IR:FRS', + label: 'Iran — Fars (فارس)', + }, + { + key: 'IR:QHM', + label: 'Iran — Qom (قم)', + }, + { + key: 'IR:KRD', + label: 'Iran — Kurdistan / کردستان)', + }, + { + key: 'IR:KBD', + label: 'Iran — Kohgiluyeh and BoyerAhmad (کهگیلوییه و بویراحمد)', + }, + { + key: 'IR:GLS', + label: 'Iran — Golestan (گلستان)', + }, + { + key: 'IR:GIL', + label: 'Iran — Gilan (گیلان)', + }, + { + key: 'IR:MZN', + label: 'Iran — Mazandaran (مازندران)', + }, + { + key: 'IR:MKZ', + label: 'Iran — Markazi (مرکزی)', + }, + { + key: 'IR:HRZ', + label: 'Iran — Hormozgan (هرمزگان)', + }, + { + key: 'IR:SBN', + label: 'Iran — Sistan and Baluchestan (سیستان و بلوچستان)', + }, + { + key: 'IQ', + label: 'Iraq', + }, + { + key: 'IE:CW', + label: 'Ireland — Carlow', + }, + { + key: 'IE:CN', + label: 'Ireland — Cavan', + }, + { + key: 'IE:CE', + label: 'Ireland — Clare', + }, + { + key: 'IE:CO', + label: 'Ireland — Cork', + }, + { + key: 'IE:DL', + label: 'Ireland — Donegal', + }, + { + key: 'IE:D', + label: 'Ireland — Dublin', + }, + { + key: 'IE:G', + label: 'Ireland — Galway', + }, + { + key: 'IE:KY', + label: 'Ireland — Kerry', + }, + { + key: 'IE:KE', + label: 'Ireland — Kildare', + }, + { + key: 'IE:KK', + label: 'Ireland — Kilkenny', + }, + { + key: 'IE:LS', + label: 'Ireland — Laois', + }, + { + key: 'IE:LM', + label: 'Ireland — Leitrim', + }, + { + key: 'IE:LK', + label: 'Ireland — Limerick', + }, + { + key: 'IE:LD', + label: 'Ireland — Longford', + }, + { + key: 'IE:LH', + label: 'Ireland — Louth', + }, + { + key: 'IE:MO', + label: 'Ireland — Mayo', + }, + { + key: 'IE:MH', + label: 'Ireland — Meath', + }, + { + key: 'IE:MN', + label: 'Ireland — Monaghan', + }, + { + key: 'IE:OY', + label: 'Ireland — Offaly', + }, + { + key: 'IE:RN', + label: 'Ireland — Roscommon', + }, + { + key: 'IE:SO', + label: 'Ireland — Sligo', + }, + { + key: 'IE:TA', + label: 'Ireland — Tipperary', + }, + { + key: 'IE:WD', + label: 'Ireland — Waterford', + }, + { + key: 'IE:WH', + label: 'Ireland — Westmeath', + }, + { + key: 'IE:WX', + label: 'Ireland — Wexford', + }, + { + key: 'IE:WW', + label: 'Ireland — Wicklow', + }, + { + key: 'IM', + label: 'Isle of Man', + }, + { + key: 'IL', + label: 'Israel', + }, + { + key: 'IT:AG', + label: 'Italy — Agrigento', + }, + { + key: 'IT:AL', + label: 'Italy — Alessandria', + }, + { + key: 'IT:AN', + label: 'Italy — Ancona', + }, + { + key: 'IT:AO', + label: 'Italy — Aosta', + }, + { + key: 'IT:AR', + label: 'Italy — Arezzo', + }, + { + key: 'IT:AP', + label: 'Italy — Ascoli Piceno', + }, + { + key: 'IT:AT', + label: 'Italy — Asti', + }, + { + key: 'IT:AV', + label: 'Italy — Avellino', + }, + { + key: 'IT:BA', + label: 'Italy — Bari', + }, + { + key: 'IT:BT', + label: 'Italy — Barletta-Andria-Trani', + }, + { + key: 'IT:BL', + label: 'Italy — Belluno', + }, + { + key: 'IT:BN', + label: 'Italy — Benevento', + }, + { + key: 'IT:BG', + label: 'Italy — Bergamo', + }, + { + key: 'IT:BI', + label: 'Italy — Biella', + }, + { + key: 'IT:BO', + label: 'Italy — Bologna', + }, + { + key: 'IT:BZ', + label: 'Italy — Bolzano', + }, + { + key: 'IT:BS', + label: 'Italy — Brescia', + }, + { + key: 'IT:BR', + label: 'Italy — Brindisi', + }, + { + key: 'IT:CA', + label: 'Italy — Cagliari', + }, + { + key: 'IT:CL', + label: 'Italy — Caltanissetta', + }, + { + key: 'IT:CB', + label: 'Italy — Campobasso', + }, + { + key: 'IT:CE', + label: 'Italy — Caserta', + }, + { + key: 'IT:CT', + label: 'Italy — Catania', + }, + { + key: 'IT:CZ', + label: 'Italy — Catanzaro', + }, + { + key: 'IT:CH', + label: 'Italy — Chieti', + }, + { + key: 'IT:CO', + label: 'Italy — Como', + }, + { + key: 'IT:CS', + label: 'Italy — Cosenza', + }, + { + key: 'IT:CR', + label: 'Italy — Cremona', + }, + { + key: 'IT:KR', + label: 'Italy — Crotone', + }, + { + key: 'IT:CN', + label: 'Italy — Cuneo', + }, + { + key: 'IT:EN', + label: 'Italy — Enna', + }, + { + key: 'IT:FM', + label: 'Italy — Fermo', + }, + { + key: 'IT:FE', + label: 'Italy — Ferrara', + }, + { + key: 'IT:FI', + label: 'Italy — Firenze', + }, + { + key: 'IT:FG', + label: 'Italy — Foggia', + }, + { + key: 'IT:FC', + label: 'Italy — Forlì-Cesena', + }, + { + key: 'IT:FR', + label: 'Italy — Frosinone', + }, + { + key: 'IT:GE', + label: 'Italy — Genova', + }, + { + key: 'IT:GO', + label: 'Italy — Gorizia', + }, + { + key: 'IT:GR', + label: 'Italy — Grosseto', + }, + { + key: 'IT:IM', + label: 'Italy — Imperia', + }, + { + key: 'IT:IS', + label: 'Italy — Isernia', + }, + { + key: 'IT:SP', + label: 'Italy — La Spezia', + }, + { + key: 'IT:AQ', + label: "Italy — L'Aquila", + }, + { + key: 'IT:LT', + label: 'Italy — Latina', + }, + { + key: 'IT:LE', + label: 'Italy — Lecce', + }, + { + key: 'IT:LC', + label: 'Italy — Lecco', + }, + { + key: 'IT:LI', + label: 'Italy — Livorno', + }, + { + key: 'IT:LO', + label: 'Italy — Lodi', + }, + { + key: 'IT:LU', + label: 'Italy — Lucca', + }, + { + key: 'IT:MC', + label: 'Italy — Macerata', + }, + { + key: 'IT:MN', + label: 'Italy — Mantova', + }, + { + key: 'IT:MS', + label: 'Italy — Massa-Carrara', + }, + { + key: 'IT:MT', + label: 'Italy — Matera', + }, + { + key: 'IT:ME', + label: 'Italy — Messina', + }, + { + key: 'IT:MI', + label: 'Italy — Milano', + }, + { + key: 'IT:MO', + label: 'Italy — Modena', + }, + { + key: 'IT:MB', + label: 'Italy — Monza e della Brianza', + }, + { + key: 'IT:NA', + label: 'Italy — Napoli', + }, + { + key: 'IT:NO', + label: 'Italy — Novara', + }, + { + key: 'IT:NU', + label: 'Italy — Nuoro', + }, + { + key: 'IT:OR', + label: 'Italy — Oristano', + }, + { + key: 'IT:PD', + label: 'Italy — Padova', + }, + { + key: 'IT:PA', + label: 'Italy — Palermo', + }, + { + key: 'IT:PR', + label: 'Italy — Parma', + }, + { + key: 'IT:PV', + label: 'Italy — Pavia', + }, + { + key: 'IT:PG', + label: 'Italy — Perugia', + }, + { + key: 'IT:PU', + label: 'Italy — Pesaro e Urbino', + }, + { + key: 'IT:PE', + label: 'Italy — Pescara', + }, + { + key: 'IT:PC', + label: 'Italy — Piacenza', + }, + { + key: 'IT:PI', + label: 'Italy — Pisa', + }, + { + key: 'IT:PT', + label: 'Italy — Pistoia', + }, + { + key: 'IT:PN', + label: 'Italy — Pordenone', + }, + { + key: 'IT:PZ', + label: 'Italy — Potenza', + }, + { + key: 'IT:PO', + label: 'Italy — Prato', + }, + { + key: 'IT:RG', + label: 'Italy — Ragusa', + }, + { + key: 'IT:RA', + label: 'Italy — Ravenna', + }, + { + key: 'IT:RC', + label: 'Italy — Reggio Calabria', + }, + { + key: 'IT:RE', + label: 'Italy — Reggio Emilia', + }, + { + key: 'IT:RI', + label: 'Italy — Rieti', + }, + { + key: 'IT:RN', + label: 'Italy — Rimini', + }, + { + key: 'IT:RM', + label: 'Italy — Roma', + }, + { + key: 'IT:RO', + label: 'Italy — Rovigo', + }, + { + key: 'IT:SA', + label: 'Italy — Salerno', + }, + { + key: 'IT:SS', + label: 'Italy — Sassari', + }, + { + key: 'IT:SV', + label: 'Italy — Savona', + }, + { + key: 'IT:SI', + label: 'Italy — Siena', + }, + { + key: 'IT:SR', + label: 'Italy — Siracusa', + }, + { + key: 'IT:SO', + label: 'Italy — Sondrio', + }, + { + key: 'IT:SU', + label: 'Italy — Sud Sardegna', + }, + { + key: 'IT:TA', + label: 'Italy — Taranto', + }, + { + key: 'IT:TE', + label: 'Italy — Teramo', + }, + { + key: 'IT:TR', + label: 'Italy — Terni', + }, + { + key: 'IT:TO', + label: 'Italy — Torino', + }, + { + key: 'IT:TP', + label: 'Italy — Trapani', + }, + { + key: 'IT:TN', + label: 'Italy — Trento', + }, + { + key: 'IT:TV', + label: 'Italy — Treviso', + }, + { + key: 'IT:TS', + label: 'Italy — Trieste', + }, + { + key: 'IT:UD', + label: 'Italy — Udine', + }, + { + key: 'IT:VA', + label: 'Italy — Varese', + }, + { + key: 'IT:VE', + label: 'Italy — Venezia', + }, + { + key: 'IT:VB', + label: 'Italy — Verbano-Cusio-Ossola', + }, + { + key: 'IT:VC', + label: 'Italy — Vercelli', + }, + { + key: 'IT:VR', + label: 'Italy — Verona', + }, + { + key: 'IT:VV', + label: 'Italy — Vibo Valentia', + }, + { + key: 'IT:VI', + label: 'Italy — Vicenza', + }, + { + key: 'IT:VT', + label: 'Italy — Viterbo', + }, + { + key: 'CI', + label: 'Ivory Coast', + }, + { + key: 'JM:JM-01', + label: 'Jamaica — Kingston', + }, + { + key: 'JM:JM-02', + label: 'Jamaica — Saint Andrew', + }, + { + key: 'JM:JM-03', + label: 'Jamaica — Saint Thomas', + }, + { + key: 'JM:JM-04', + label: 'Jamaica — Portland', + }, + { + key: 'JM:JM-05', + label: 'Jamaica — Saint Mary', + }, + { + key: 'JM:JM-06', + label: 'Jamaica — Saint Ann', + }, + { + key: 'JM:JM-07', + label: 'Jamaica — Trelawny', + }, + { + key: 'JM:JM-08', + label: 'Jamaica — Saint James', + }, + { + key: 'JM:JM-09', + label: 'Jamaica — Hanover', + }, + { + key: 'JM:JM-10', + label: 'Jamaica — Westmoreland', + }, + { + key: 'JM:JM-11', + label: 'Jamaica — Saint Elizabeth', + }, + { + key: 'JM:JM-12', + label: 'Jamaica — Manchester', + }, + { + key: 'JM:JM-13', + label: 'Jamaica — Clarendon', + }, + { + key: 'JM:JM-14', + label: 'Jamaica — Saint Catherine', + }, + { + key: 'JP:JP01', + label: 'Japan — Hokkaido', + }, + { + key: 'JP:JP02', + label: 'Japan — Aomori', + }, + { + key: 'JP:JP03', + label: 'Japan — Iwate', + }, + { + key: 'JP:JP04', + label: 'Japan — Miyagi', + }, + { + key: 'JP:JP05', + label: 'Japan — Akita', + }, + { + key: 'JP:JP06', + label: 'Japan — Yamagata', + }, + { + key: 'JP:JP07', + label: 'Japan — Fukushima', + }, + { + key: 'JP:JP08', + label: 'Japan — Ibaraki', + }, + { + key: 'JP:JP09', + label: 'Japan — Tochigi', + }, + { + key: 'JP:JP10', + label: 'Japan — Gunma', + }, + { + key: 'JP:JP11', + label: 'Japan — Saitama', + }, + { + key: 'JP:JP12', + label: 'Japan — Chiba', + }, + { + key: 'JP:JP13', + label: 'Japan — Tokyo', + }, + { + key: 'JP:JP14', + label: 'Japan — Kanagawa', + }, + { + key: 'JP:JP15', + label: 'Japan — Niigata', + }, + { + key: 'JP:JP16', + label: 'Japan — Toyama', + }, + { + key: 'JP:JP17', + label: 'Japan — Ishikawa', + }, + { + key: 'JP:JP18', + label: 'Japan — Fukui', + }, + { + key: 'JP:JP19', + label: 'Japan — Yamanashi', + }, + { + key: 'JP:JP20', + label: 'Japan — Nagano', + }, + { + key: 'JP:JP21', + label: 'Japan — Gifu', + }, + { + key: 'JP:JP22', + label: 'Japan — Shizuoka', + }, + { + key: 'JP:JP23', + label: 'Japan — Aichi', + }, + { + key: 'JP:JP24', + label: 'Japan — Mie', + }, + { + key: 'JP:JP25', + label: 'Japan — Shiga', + }, + { + key: 'JP:JP26', + label: 'Japan — Kyoto', + }, + { + key: 'JP:JP27', + label: 'Japan — Osaka', + }, + { + key: 'JP:JP28', + label: 'Japan — Hyogo', + }, + { + key: 'JP:JP29', + label: 'Japan — Nara', + }, + { + key: 'JP:JP30', + label: 'Japan — Wakayama', + }, + { + key: 'JP:JP31', + label: 'Japan — Tottori', + }, + { + key: 'JP:JP32', + label: 'Japan — Shimane', + }, + { + key: 'JP:JP33', + label: 'Japan — Okayama', + }, + { + key: 'JP:JP34', + label: 'Japan — Hiroshima', + }, + { + key: 'JP:JP35', + label: 'Japan — Yamaguchi', + }, + { + key: 'JP:JP36', + label: 'Japan — Tokushima', + }, + { + key: 'JP:JP37', + label: 'Japan — Kagawa', + }, + { + key: 'JP:JP38', + label: 'Japan — Ehime', + }, + { + key: 'JP:JP39', + label: 'Japan — Kochi', + }, + { + key: 'JP:JP40', + label: 'Japan — Fukuoka', + }, + { + key: 'JP:JP41', + label: 'Japan — Saga', + }, + { + key: 'JP:JP42', + label: 'Japan — Nagasaki', + }, + { + key: 'JP:JP43', + label: 'Japan — Kumamoto', + }, + { + key: 'JP:JP44', + label: 'Japan — Oita', + }, + { + key: 'JP:JP45', + label: 'Japan — Miyazaki', + }, + { + key: 'JP:JP46', + label: 'Japan — Kagoshima', + }, + { + key: 'JP:JP47', + label: 'Japan — Okinawa', + }, + { + key: 'JE', + label: 'Jersey', + }, + { + key: 'JO', + label: 'Jordan', + }, + { + key: 'KZ', + label: 'Kazakhstan', + }, + { + key: 'KE:KE01', + label: 'Kenya — Baringo', + }, + { + key: 'KE:KE02', + label: 'Kenya — Bomet', + }, + { + key: 'KE:KE03', + label: 'Kenya — Bungoma', + }, + { + key: 'KE:KE04', + label: 'Kenya — Busia', + }, + { + key: 'KE:KE05', + label: 'Kenya — Elgeyo-Marakwet', + }, + { + key: 'KE:KE06', + label: 'Kenya — Embu', + }, + { + key: 'KE:KE07', + label: 'Kenya — Garissa', + }, + { + key: 'KE:KE08', + label: 'Kenya — Homa Bay', + }, + { + key: 'KE:KE09', + label: 'Kenya — Isiolo', + }, + { + key: 'KE:KE10', + label: 'Kenya — Kajiado', + }, + { + key: 'KE:KE11', + label: 'Kenya — Kakamega', + }, + { + key: 'KE:KE12', + label: 'Kenya — Kericho', + }, + { + key: 'KE:KE13', + label: 'Kenya — Kiambu', + }, + { + key: 'KE:KE14', + label: 'Kenya — Kilifi', + }, + { + key: 'KE:KE15', + label: 'Kenya — Kirinyaga', + }, + { + key: 'KE:KE16', + label: 'Kenya — Kisii', + }, + { + key: 'KE:KE17', + label: 'Kenya — Kisumu', + }, + { + key: 'KE:KE18', + label: 'Kenya — Kitui', + }, + { + key: 'KE:KE19', + label: 'Kenya — Kwale', + }, + { + key: 'KE:KE20', + label: 'Kenya — Laikipia', + }, + { + key: 'KE:KE21', + label: 'Kenya — Lamu', + }, + { + key: 'KE:KE22', + label: 'Kenya — Machakos', + }, + { + key: 'KE:KE23', + label: 'Kenya — Makueni', + }, + { + key: 'KE:KE24', + label: 'Kenya — Mandera', + }, + { + key: 'KE:KE25', + label: 'Kenya — Marsabit', + }, + { + key: 'KE:KE26', + label: 'Kenya — Meru', + }, + { + key: 'KE:KE27', + label: 'Kenya — Migori', + }, + { + key: 'KE:KE28', + label: 'Kenya — Mombasa', + }, + { + key: 'KE:KE29', + label: 'Kenya — Murang’a', + }, + { + key: 'KE:KE30', + label: 'Kenya — Nairobi County', + }, + { + key: 'KE:KE31', + label: 'Kenya — Nakuru', + }, + { + key: 'KE:KE32', + label: 'Kenya — Nandi', + }, + { + key: 'KE:KE33', + label: 'Kenya — Narok', + }, + { + key: 'KE:KE34', + label: 'Kenya — Nyamira', + }, + { + key: 'KE:KE35', + label: 'Kenya — Nyandarua', + }, + { + key: 'KE:KE36', + label: 'Kenya — Nyeri', + }, + { + key: 'KE:KE37', + label: 'Kenya — Samburu', + }, + { + key: 'KE:KE38', + label: 'Kenya — Siaya', + }, + { + key: 'KE:KE39', + label: 'Kenya — Taita-Taveta', + }, + { + key: 'KE:KE40', + label: 'Kenya — Tana River', + }, + { + key: 'KE:KE41', + label: 'Kenya — Tharaka-Nithi', + }, + { + key: 'KE:KE42', + label: 'Kenya — Trans Nzoia', + }, + { + key: 'KE:KE43', + label: 'Kenya — Turkana', + }, + { + key: 'KE:KE44', + label: 'Kenya — Uasin Gishu', + }, + { + key: 'KE:KE45', + label: 'Kenya — Vihiga', + }, + { + key: 'KE:KE46', + label: 'Kenya — Wajir', + }, + { + key: 'KE:KE47', + label: 'Kenya — West Pokot', + }, + { + key: 'KI', + label: 'Kiribati', + }, + { + key: 'KW', + label: 'Kuwait', + }, + { + key: 'KG', + label: 'Kyrgyzstan', + }, + { + key: 'LA:AT', + label: 'Laos — Attapeu', + }, + { + key: 'LA:BK', + label: 'Laos — Bokeo', + }, + { + key: 'LA:BL', + label: 'Laos — Bolikhamsai', + }, + { + key: 'LA:CH', + label: 'Laos — Champasak', + }, + { + key: 'LA:HO', + label: 'Laos — Houaphanh', + }, + { + key: 'LA:KH', + label: 'Laos — Khammouane', + }, + { + key: 'LA:LM', + label: 'Laos — Luang Namtha', + }, + { + key: 'LA:LP', + label: 'Laos — Luang Prabang', + }, + { + key: 'LA:OU', + label: 'Laos — Oudomxay', + }, + { + key: 'LA:PH', + label: 'Laos — Phongsaly', + }, + { + key: 'LA:SL', + label: 'Laos — Salavan', + }, + { + key: 'LA:SV', + label: 'Laos — Savannakhet', + }, + { + key: 'LA:VI', + label: 'Laos — Vientiane Province', + }, + { + key: 'LA:VT', + label: 'Laos — Vientiane', + }, + { + key: 'LA:XA', + label: 'Laos — Sainyabuli', + }, + { + key: 'LA:XE', + label: 'Laos — Sekong', + }, + { + key: 'LA:XI', + label: 'Laos — Xiangkhouang', + }, + { + key: 'LA:XS', + label: 'Laos — Xaisomboun', + }, + { + key: 'LV', + label: 'Latvia', + }, + { + key: 'LB', + label: 'Lebanon', + }, + { + key: 'LS', + label: 'Lesotho', + }, + { + key: 'LR:BM', + label: 'Liberia — Bomi', + }, + { + key: 'LR:BN', + label: 'Liberia — Bong', + }, + { + key: 'LR:GA', + label: 'Liberia — Gbarpolu', + }, + { + key: 'LR:GB', + label: 'Liberia — Grand Bassa', + }, + { + key: 'LR:GC', + label: 'Liberia — Grand Cape Mount', + }, + { + key: 'LR:GG', + label: 'Liberia — Grand Gedeh', + }, + { + key: 'LR:GK', + label: 'Liberia — Grand Kru', + }, + { + key: 'LR:LO', + label: 'Liberia — Lofa', + }, + { + key: 'LR:MA', + label: 'Liberia — Margibi', + }, + { + key: 'LR:MY', + label: 'Liberia — Maryland', + }, + { + key: 'LR:MO', + label: 'Liberia — Montserrado', + }, + { + key: 'LR:NM', + label: 'Liberia — Nimba', + }, + { + key: 'LR:RV', + label: 'Liberia — Rivercess', + }, + { + key: 'LR:RG', + label: 'Liberia — River Gee', + }, + { + key: 'LR:SN', + label: 'Liberia — Sinoe', + }, + { + key: 'LY', + label: 'Libya', + }, + { + key: 'LI', + label: 'Liechtenstein', + }, + { + key: 'LT', + label: 'Lithuania', + }, + { + key: 'LU', + label: 'Luxembourg', + }, + { + key: 'MO', + label: 'Macao', + }, + { + key: 'MG', + label: 'Madagascar', + }, + { + key: 'MW', + label: 'Malawi', + }, + { + key: 'MY:JHR', + label: 'Malaysia — Johor', + }, + { + key: 'MY:KDH', + label: 'Malaysia — Kedah', + }, + { + key: 'MY:KTN', + label: 'Malaysia — Kelantan', + }, + { + key: 'MY:LBN', + label: 'Malaysia — Labuan', + }, + { + key: 'MY:MLK', + label: 'Malaysia — Malacca (Melaka)', + }, + { + key: 'MY:NSN', + label: 'Malaysia — Negeri Sembilan', + }, + { + key: 'MY:PHG', + label: 'Malaysia — Pahang', + }, + { + key: 'MY:PNG', + label: 'Malaysia — Penang (Pulau Pinang)', + }, + { + key: 'MY:PRK', + label: 'Malaysia — Perak', + }, + { + key: 'MY:PLS', + label: 'Malaysia — Perlis', + }, + { + key: 'MY:SBH', + label: 'Malaysia — Sabah', + }, + { + key: 'MY:SWK', + label: 'Malaysia — Sarawak', + }, + { + key: 'MY:SGR', + label: 'Malaysia — Selangor', + }, + { + key: 'MY:TRG', + label: 'Malaysia — Terengganu', + }, + { + key: 'MY:PJY', + label: 'Malaysia — Putrajaya', + }, + { + key: 'MY:KUL', + label: 'Malaysia — Kuala Lumpur', + }, + { + key: 'MV', + label: 'Maldives', + }, + { + key: 'ML', + label: 'Mali', + }, + { + key: 'MT', + label: 'Malta', + }, + { + key: 'MH', + label: 'Marshall Islands', + }, + { + key: 'MQ', + label: 'Martinique', + }, + { + key: 'MR', + label: 'Mauritania', + }, + { + key: 'MU', + label: 'Mauritius', + }, + { + key: 'YT', + label: 'Mayotte', + }, + { + key: 'MX:DF', + label: 'Mexico — Ciudad de México', + }, + { + key: 'MX:JA', + label: 'Mexico — Jalisco', + }, + { + key: 'MX:NL', + label: 'Mexico — Nuevo León', + }, + { + key: 'MX:AG', + label: 'Mexico — Aguascalientes', + }, + { + key: 'MX:BC', + label: 'Mexico — Baja California', + }, + { + key: 'MX:BS', + label: 'Mexico — Baja California Sur', + }, + { + key: 'MX:CM', + label: 'Mexico — Campeche', + }, + { + key: 'MX:CS', + label: 'Mexico — Chiapas', + }, + { + key: 'MX:CH', + label: 'Mexico — Chihuahua', + }, + { + key: 'MX:CO', + label: 'Mexico — Coahuila', + }, + { + key: 'MX:CL', + label: 'Mexico — Colima', + }, + { + key: 'MX:DG', + label: 'Mexico — Durango', + }, + { + key: 'MX:GT', + label: 'Mexico — Guanajuato', + }, + { + key: 'MX:GR', + label: 'Mexico — Guerrero', + }, + { + key: 'MX:HG', + label: 'Mexico — Hidalgo', + }, + { + key: 'MX:MX', + label: 'Mexico — Estado de México', + }, + { + key: 'MX:MI', + label: 'Mexico — Michoacán', + }, + { + key: 'MX:MO', + label: 'Mexico — Morelos', + }, + { + key: 'MX:NA', + label: 'Mexico — Nayarit', + }, + { + key: 'MX:OA', + label: 'Mexico — Oaxaca', + }, + { + key: 'MX:PU', + label: 'Mexico — Puebla', + }, + { + key: 'MX:QT', + label: 'Mexico — Querétaro', + }, + { + key: 'MX:QR', + label: 'Mexico — Quintana Roo', + }, + { + key: 'MX:SL', + label: 'Mexico — San Luis Potosí', + }, + { + key: 'MX:SI', + label: 'Mexico — Sinaloa', + }, + { + key: 'MX:SO', + label: 'Mexico — Sonora', + }, + { + key: 'MX:TB', + label: 'Mexico — Tabasco', + }, + { + key: 'MX:TM', + label: 'Mexico — Tamaulipas', + }, + { + key: 'MX:TL', + label: 'Mexico — Tlaxcala', + }, + { + key: 'MX:VE', + label: 'Mexico — Veracruz', + }, + { + key: 'MX:YU', + label: 'Mexico — Yucatán', + }, + { + key: 'MX:ZA', + label: 'Mexico — Zacatecas', + }, + { + key: 'FM', + label: 'Micronesia', + }, + { + key: 'MD:C', + label: 'Moldova — Chișinău', + }, + { + key: 'MD:BL', + label: 'Moldova — Bălți', + }, + { + key: 'MD:AN', + label: 'Moldova — Anenii Noi', + }, + { + key: 'MD:BS', + label: 'Moldova — Basarabeasca', + }, + { + key: 'MD:BR', + label: 'Moldova — Briceni', + }, + { + key: 'MD:CH', + label: 'Moldova — Cahul', + }, + { + key: 'MD:CT', + label: 'Moldova — Cantemir', + }, + { + key: 'MD:CL', + label: 'Moldova — Călărași', + }, + { + key: 'MD:CS', + label: 'Moldova — Căușeni', + }, + { + key: 'MD:CM', + label: 'Moldova — Cimișlia', + }, + { + key: 'MD:CR', + label: 'Moldova — Criuleni', + }, + { + key: 'MD:DN', + label: 'Moldova — Dondușeni', + }, + { + key: 'MD:DR', + label: 'Moldova — Drochia', + }, + { + key: 'MD:DB', + label: 'Moldova — Dubăsari', + }, + { + key: 'MD:ED', + label: 'Moldova — Edineț', + }, + { + key: 'MD:FL', + label: 'Moldova — Fălești', + }, + { + key: 'MD:FR', + label: 'Moldova — Florești', + }, + { + key: 'MD:GE', + label: 'Moldova — UTA Găgăuzia', + }, + { + key: 'MD:GL', + label: 'Moldova — Glodeni', + }, + { + key: 'MD:HN', + label: 'Moldova — Hîncești', + }, + { + key: 'MD:IL', + label: 'Moldova — Ialoveni', + }, + { + key: 'MD:LV', + label: 'Moldova — Leova', + }, + { + key: 'MD:NS', + label: 'Moldova — Nisporeni', + }, + { + key: 'MD:OC', + label: 'Moldova — Ocnița', + }, + { + key: 'MD:OR', + label: 'Moldova — Orhei', + }, + { + key: 'MD:RZ', + label: 'Moldova — Rezina', + }, + { + key: 'MD:RS', + label: 'Moldova — Rîșcani', + }, + { + key: 'MD:SG', + label: 'Moldova — Sîngerei', + }, + { + key: 'MD:SR', + label: 'Moldova — Soroca', + }, + { + key: 'MD:ST', + label: 'Moldova — Strășeni', + }, + { + key: 'MD:SD', + label: 'Moldova — Șoldănești', + }, + { + key: 'MD:SV', + label: 'Moldova — Ștefan Vodă', + }, + { + key: 'MD:TR', + label: 'Moldova — Taraclia', + }, + { + key: 'MD:TL', + label: 'Moldova — Telenești', + }, + { + key: 'MD:UN', + label: 'Moldova — Ungheni', + }, + { + key: 'MC', + label: 'Monaco', + }, + { + key: 'MN', + label: 'Mongolia', + }, + { + key: 'ME', + label: 'Montenegro', + }, + { + key: 'MS', + label: 'Montserrat', + }, + { + key: 'MA', + label: 'Morocco', + }, + { + key: 'MZ:MZP', + label: 'Mozambique — Cabo Delgado', + }, + { + key: 'MZ:MZG', + label: 'Mozambique — Gaza', + }, + { + key: 'MZ:MZI', + label: 'Mozambique — Inhambane', + }, + { + key: 'MZ:MZB', + label: 'Mozambique — Manica', + }, + { + key: 'MZ:MZL', + label: 'Mozambique — Maputo Province', + }, + { + key: 'MZ:MZMPM', + label: 'Mozambique — Maputo', + }, + { + key: 'MZ:MZN', + label: 'Mozambique — Nampula', + }, + { + key: 'MZ:MZA', + label: 'Mozambique — Niassa', + }, + { + key: 'MZ:MZS', + label: 'Mozambique — Sofala', + }, + { + key: 'MZ:MZT', + label: 'Mozambique — Tete', + }, + { + key: 'MZ:MZQ', + label: 'Mozambique — Zambézia', + }, + { + key: 'MM', + label: 'Myanmar', + }, + { + key: 'NA:ER', + label: 'Namibia — Erongo', + }, + { + key: 'NA:HA', + label: 'Namibia — Hardap', + }, + { + key: 'NA:KA', + label: 'Namibia — Karas', + }, + { + key: 'NA:KE', + label: 'Namibia — Kavango East', + }, + { + key: 'NA:KW', + label: 'Namibia — Kavango West', + }, + { + key: 'NA:KH', + label: 'Namibia — Khomas', + }, + { + key: 'NA:KU', + label: 'Namibia — Kunene', + }, + { + key: 'NA:OW', + label: 'Namibia — Ohangwena', + }, + { + key: 'NA:OH', + label: 'Namibia — Omaheke', + }, + { + key: 'NA:OS', + label: 'Namibia — Omusati', + }, + { + key: 'NA:ON', + label: 'Namibia — Oshana', + }, + { + key: 'NA:OT', + label: 'Namibia — Oshikoto', + }, + { + key: 'NA:OD', + label: 'Namibia — Otjozondjupa', + }, + { + key: 'NA:CA', + label: 'Namibia — Zambezi', + }, + { + key: 'NR', + label: 'Nauru', + }, + { + key: 'NP:BAG', + label: 'Nepal — Bagmati', + }, + { + key: 'NP:BHE', + label: 'Nepal — Bheri', + }, + { + key: 'NP:DHA', + label: 'Nepal — Dhaulagiri', + }, + { + key: 'NP:GAN', + label: 'Nepal — Gandaki', + }, + { + key: 'NP:JAN', + label: 'Nepal — Janakpur', + }, + { + key: 'NP:KAR', + label: 'Nepal — Karnali', + }, + { + key: 'NP:KOS', + label: 'Nepal — Koshi', + }, + { + key: 'NP:LUM', + label: 'Nepal — Lumbini', + }, + { + key: 'NP:MAH', + label: 'Nepal — Mahakali', + }, + { + key: 'NP:MEC', + label: 'Nepal — Mechi', + }, + { + key: 'NP:NAR', + label: 'Nepal — Narayani', + }, + { + key: 'NP:RAP', + label: 'Nepal — Rapti', + }, + { + key: 'NP:SAG', + label: 'Nepal — Sagarmatha', + }, + { + key: 'NP:SET', + label: 'Nepal — Seti', + }, + { + key: 'NL', + label: 'Netherlands', + }, + { + key: 'NC', + label: 'New Caledonia', + }, + { + key: 'NZ:NTL', + label: 'New Zealand — Northland', + }, + { + key: 'NZ:AUK', + label: 'New Zealand — Auckland', + }, + { + key: 'NZ:WKO', + label: 'New Zealand — Waikato', + }, + { + key: 'NZ:BOP', + label: 'New Zealand — Bay of Plenty', + }, + { + key: 'NZ:TKI', + label: 'New Zealand — Taranaki', + }, + { + key: 'NZ:GIS', + label: 'New Zealand — Gisborne', + }, + { + key: 'NZ:HKB', + label: 'New Zealand — Hawke’s Bay', + }, + { + key: 'NZ:MWT', + label: 'New Zealand — Manawatu-Wanganui', + }, + { + key: 'NZ:WGN', + label: 'New Zealand — Wellington', + }, + { + key: 'NZ:NSN', + label: 'New Zealand — Nelson', + }, + { + key: 'NZ:MBH', + label: 'New Zealand — Marlborough', + }, + { + key: 'NZ:TAS', + label: 'New Zealand — Tasman', + }, + { + key: 'NZ:WTC', + label: 'New Zealand — West Coast', + }, + { + key: 'NZ:CAN', + label: 'New Zealand — Canterbury', + }, + { + key: 'NZ:OTA', + label: 'New Zealand — Otago', + }, + { + key: 'NZ:STL', + label: 'New Zealand — Southland', + }, + { + key: 'NI:NI-AN', + label: 'Nicaragua — Atlántico Norte', + }, + { + key: 'NI:NI-AS', + label: 'Nicaragua — Atlántico Sur', + }, + { + key: 'NI:NI-BO', + label: 'Nicaragua — Boaco', + }, + { + key: 'NI:NI-CA', + label: 'Nicaragua — Carazo', + }, + { + key: 'NI:NI-CI', + label: 'Nicaragua — Chinandega', + }, + { + key: 'NI:NI-CO', + label: 'Nicaragua — Chontales', + }, + { + key: 'NI:NI-ES', + label: 'Nicaragua — Estelí', + }, + { + key: 'NI:NI-GR', + label: 'Nicaragua — Granada', + }, + { + key: 'NI:NI-JI', + label: 'Nicaragua — Jinotega', + }, + { + key: 'NI:NI-LE', + label: 'Nicaragua — León', + }, + { + key: 'NI:NI-MD', + label: 'Nicaragua — Madriz', + }, + { + key: 'NI:NI-MN', + label: 'Nicaragua — Managua', + }, + { + key: 'NI:NI-MS', + label: 'Nicaragua — Masaya', + }, + { + key: 'NI:NI-MT', + label: 'Nicaragua — Matagalpa', + }, + { + key: 'NI:NI-NS', + label: 'Nicaragua — Nueva Segovia', + }, + { + key: 'NI:NI-RI', + label: 'Nicaragua — Rivas', + }, + { + key: 'NI:NI-SJ', + label: 'Nicaragua — Río San Juan', + }, + { + key: 'NE', + label: 'Niger', + }, + { + key: 'NG:AB', + label: 'Nigeria — Abia', + }, + { + key: 'NG:FC', + label: 'Nigeria — Abuja', + }, + { + key: 'NG:AD', + label: 'Nigeria — Adamawa', + }, + { + key: 'NG:AK', + label: 'Nigeria — Akwa Ibom', + }, + { + key: 'NG:AN', + label: 'Nigeria — Anambra', + }, + { + key: 'NG:BA', + label: 'Nigeria — Bauchi', + }, + { + key: 'NG:BY', + label: 'Nigeria — Bayelsa', + }, + { + key: 'NG:BE', + label: 'Nigeria — Benue', + }, + { + key: 'NG:BO', + label: 'Nigeria — Borno', + }, + { + key: 'NG:CR', + label: 'Nigeria — Cross River', + }, + { + key: 'NG:DE', + label: 'Nigeria — Delta', + }, + { + key: 'NG:EB', + label: 'Nigeria — Ebonyi', + }, + { + key: 'NG:ED', + label: 'Nigeria — Edo', + }, + { + key: 'NG:EK', + label: 'Nigeria — Ekiti', + }, + { + key: 'NG:EN', + label: 'Nigeria — Enugu', + }, + { + key: 'NG:GO', + label: 'Nigeria — Gombe', + }, + { + key: 'NG:IM', + label: 'Nigeria — Imo', + }, + { + key: 'NG:JI', + label: 'Nigeria — Jigawa', + }, + { + key: 'NG:KD', + label: 'Nigeria — Kaduna', + }, + { + key: 'NG:KN', + label: 'Nigeria — Kano', + }, + { + key: 'NG:KT', + label: 'Nigeria — Katsina', + }, + { + key: 'NG:KE', + label: 'Nigeria — Kebbi', + }, + { + key: 'NG:KO', + label: 'Nigeria — Kogi', + }, + { + key: 'NG:KW', + label: 'Nigeria — Kwara', + }, + { + key: 'NG:LA', + label: 'Nigeria — Lagos', + }, + { + key: 'NG:NA', + label: 'Nigeria — Nasarawa', + }, + { + key: 'NG:NI', + label: 'Nigeria — Niger', + }, + { + key: 'NG:OG', + label: 'Nigeria — Ogun', + }, + { + key: 'NG:ON', + label: 'Nigeria — Ondo', + }, + { + key: 'NG:OS', + label: 'Nigeria — Osun', + }, + { + key: 'NG:OY', + label: 'Nigeria — Oyo', + }, + { + key: 'NG:PL', + label: 'Nigeria — Plateau', + }, + { + key: 'NG:RI', + label: 'Nigeria — Rivers', + }, + { + key: 'NG:SO', + label: 'Nigeria — Sokoto', + }, + { + key: 'NG:TA', + label: 'Nigeria — Taraba', + }, + { + key: 'NG:YO', + label: 'Nigeria — Yobe', + }, + { + key: 'NG:ZA', + label: 'Nigeria — Zamfara', + }, + { + key: 'NU', + label: 'Niue', + }, + { + key: 'NF', + label: 'Norfolk Island', + }, + { + key: 'KP', + label: 'North Korea', + }, + { + key: 'MK', + label: 'North Macedonia', + }, + { + key: 'MP', + label: 'Northern Mariana Islands', + }, + { + key: 'NO', + label: 'Norway', + }, + { + key: 'OM', + label: 'Oman', + }, + { + key: 'PK:JK', + label: 'Pakistan — Azad Kashmir', + }, + { + key: 'PK:BA', + label: 'Pakistan — Balochistan', + }, + { + key: 'PK:TA', + label: 'Pakistan — FATA', + }, + { + key: 'PK:GB', + label: 'Pakistan — Gilgit Baltistan', + }, + { + key: 'PK:IS', + label: 'Pakistan — Islamabad Capital Territory', + }, + { + key: 'PK:KP', + label: 'Pakistan — Khyber Pakhtunkhwa', + }, + { + key: 'PK:PB', + label: 'Pakistan — Punjab', + }, + { + key: 'PK:SD', + label: 'Pakistan — Sindh', + }, + { + key: 'PS', + label: 'Palestinian Territory', + }, + { + key: 'PA:PA-1', + label: 'Panama — Bocas del Toro', + }, + { + key: 'PA:PA-2', + label: 'Panama — Coclé', + }, + { + key: 'PA:PA-3', + label: 'Panama — Colón', + }, + { + key: 'PA:PA-4', + label: 'Panama — Chiriquí', + }, + { + key: 'PA:PA-5', + label: 'Panama — Darién', + }, + { + key: 'PA:PA-6', + label: 'Panama — Herrera', + }, + { + key: 'PA:PA-7', + label: 'Panama — Los Santos', + }, + { + key: 'PA:PA-8', + label: 'Panama — Panamá', + }, + { + key: 'PA:PA-9', + label: 'Panama — Veraguas', + }, + { + key: 'PA:PA-10', + label: 'Panama — West Panamá', + }, + { + key: 'PA:PA-EM', + label: 'Panama — Emberá', + }, + { + key: 'PA:PA-KY', + label: 'Panama — Guna Yala', + }, + { + key: 'PA:PA-NB', + label: 'Panama — Ngöbe-Buglé', + }, + { + key: 'PG', + label: 'Papua New Guinea', + }, + { + key: 'PY:PY-ASU', + label: 'Paraguay — Asunción', + }, + { + key: 'PY:PY-1', + label: 'Paraguay — Concepción', + }, + { + key: 'PY:PY-2', + label: 'Paraguay — San Pedro', + }, + { + key: 'PY:PY-3', + label: 'Paraguay — Cordillera', + }, + { + key: 'PY:PY-4', + label: 'Paraguay — Guairá', + }, + { + key: 'PY:PY-5', + label: 'Paraguay — Caaguazú', + }, + { + key: 'PY:PY-6', + label: 'Paraguay — Caazapá', + }, + { + key: 'PY:PY-7', + label: 'Paraguay — Itapúa', + }, + { + key: 'PY:PY-8', + label: 'Paraguay — Misiones', + }, + { + key: 'PY:PY-9', + label: 'Paraguay — Paraguarí', + }, + { + key: 'PY:PY-10', + label: 'Paraguay — Alto Paraná', + }, + { + key: 'PY:PY-11', + label: 'Paraguay — Central', + }, + { + key: 'PY:PY-12', + label: 'Paraguay — Ñeembucú', + }, + { + key: 'PY:PY-13', + label: 'Paraguay — Amambay', + }, + { + key: 'PY:PY-14', + label: 'Paraguay — Canindeyú', + }, + { + key: 'PY:PY-15', + label: 'Paraguay — Presidente Hayes', + }, + { + key: 'PY:PY-16', + label: 'Paraguay — Alto Paraguay', + }, + { + key: 'PY:PY-17', + label: 'Paraguay — Boquerón', + }, + { + key: 'PE:CAL', + label: 'Peru — El Callao', + }, + { + key: 'PE:LMA', + label: 'Peru — Municipalidad Metropolitana de Lima', + }, + { + key: 'PE:AMA', + label: 'Peru — Amazonas', + }, + { + key: 'PE:ANC', + label: 'Peru — Ancash', + }, + { + key: 'PE:APU', + label: 'Peru — Apurímac', + }, + { + key: 'PE:ARE', + label: 'Peru — Arequipa', + }, + { + key: 'PE:AYA', + label: 'Peru — Ayacucho', + }, + { + key: 'PE:CAJ', + label: 'Peru — Cajamarca', + }, + { + key: 'PE:CUS', + label: 'Peru — Cusco', + }, + { + key: 'PE:HUV', + label: 'Peru — Huancavelica', + }, + { + key: 'PE:HUC', + label: 'Peru — Huánuco', + }, + { + key: 'PE:ICA', + label: 'Peru — Ica', + }, + { + key: 'PE:JUN', + label: 'Peru — Junín', + }, + { + key: 'PE:LAL', + label: 'Peru — La Libertad', + }, + { + key: 'PE:LAM', + label: 'Peru — Lambayeque', + }, + { + key: 'PE:LIM', + label: 'Peru — Lima', + }, + { + key: 'PE:LOR', + label: 'Peru — Loreto', + }, + { + key: 'PE:MDD', + label: 'Peru — Madre de Dios', + }, + { + key: 'PE:MOQ', + label: 'Peru — Moquegua', + }, + { + key: 'PE:PAS', + label: 'Peru — Pasco', + }, + { + key: 'PE:PIU', + label: 'Peru — Piura', + }, + { + key: 'PE:PUN', + label: 'Peru — Puno', + }, + { + key: 'PE:SAM', + label: 'Peru — San Martín', + }, + { + key: 'PE:TAC', + label: 'Peru — Tacna', + }, + { + key: 'PE:TUM', + label: 'Peru — Tumbes', + }, + { + key: 'PE:UCA', + label: 'Peru — Ucayali', + }, + { + key: 'PH:ABR', + label: 'Philippines — Abra', + }, + { + key: 'PH:AGN', + label: 'Philippines — Agusan del Norte', + }, + { + key: 'PH:AGS', + label: 'Philippines — Agusan del Sur', + }, + { + key: 'PH:AKL', + label: 'Philippines — Aklan', + }, + { + key: 'PH:ALB', + label: 'Philippines — Albay', + }, + { + key: 'PH:ANT', + label: 'Philippines — Antique', + }, + { + key: 'PH:APA', + label: 'Philippines — Apayao', + }, + { + key: 'PH:AUR', + label: 'Philippines — Aurora', + }, + { + key: 'PH:BAS', + label: 'Philippines — Basilan', + }, + { + key: 'PH:BAN', + label: 'Philippines — Bataan', + }, + { + key: 'PH:BTN', + label: 'Philippines — Batanes', + }, + { + key: 'PH:BTG', + label: 'Philippines — Batangas', + }, + { + key: 'PH:BEN', + label: 'Philippines — Benguet', + }, + { + key: 'PH:BIL', + label: 'Philippines — Biliran', + }, + { + key: 'PH:BOH', + label: 'Philippines — Bohol', + }, + { + key: 'PH:BUK', + label: 'Philippines — Bukidnon', + }, + { + key: 'PH:BUL', + label: 'Philippines — Bulacan', + }, + { + key: 'PH:CAG', + label: 'Philippines — Cagayan', + }, + { + key: 'PH:CAN', + label: 'Philippines — Camarines Norte', + }, + { + key: 'PH:CAS', + label: 'Philippines — Camarines Sur', + }, + { + key: 'PH:CAM', + label: 'Philippines — Camiguin', + }, + { + key: 'PH:CAP', + label: 'Philippines — Capiz', + }, + { + key: 'PH:CAT', + label: 'Philippines — Catanduanes', + }, + { + key: 'PH:CAV', + label: 'Philippines — Cavite', + }, + { + key: 'PH:CEB', + label: 'Philippines — Cebu', + }, + { + key: 'PH:COM', + label: 'Philippines — Compostela Valley', + }, + { + key: 'PH:NCO', + label: 'Philippines — Cotabato', + }, + { + key: 'PH:DAV', + label: 'Philippines — Davao del Norte', + }, + { + key: 'PH:DAS', + label: 'Philippines — Davao del Sur', + }, + { + key: 'PH:DAC', + label: 'Philippines — Davao Occidental', + }, + { + key: 'PH:DAO', + label: 'Philippines — Davao Oriental', + }, + { + key: 'PH:DIN', + label: 'Philippines — Dinagat Islands', + }, + { + key: 'PH:EAS', + label: 'Philippines — Eastern Samar', + }, + { + key: 'PH:GUI', + label: 'Philippines — Guimaras', + }, + { + key: 'PH:IFU', + label: 'Philippines — Ifugao', + }, + { + key: 'PH:ILN', + label: 'Philippines — Ilocos Norte', + }, + { + key: 'PH:ILS', + label: 'Philippines — Ilocos Sur', + }, + { + key: 'PH:ILI', + label: 'Philippines — Iloilo', + }, + { + key: 'PH:ISA', + label: 'Philippines — Isabela', + }, + { + key: 'PH:KAL', + label: 'Philippines — Kalinga', + }, + { + key: 'PH:LUN', + label: 'Philippines — La Union', + }, + { + key: 'PH:LAG', + label: 'Philippines — Laguna', + }, + { + key: 'PH:LAN', + label: 'Philippines — Lanao del Norte', + }, + { + key: 'PH:LAS', + label: 'Philippines — Lanao del Sur', + }, + { + key: 'PH:LEY', + label: 'Philippines — Leyte', + }, + { + key: 'PH:MAG', + label: 'Philippines — Maguindanao', + }, + { + key: 'PH:MAD', + label: 'Philippines — Marinduque', + }, + { + key: 'PH:MAS', + label: 'Philippines — Masbate', + }, + { + key: 'PH:MSC', + label: 'Philippines — Misamis Occidental', + }, + { + key: 'PH:MSR', + label: 'Philippines — Misamis Oriental', + }, + { + key: 'PH:MOU', + label: 'Philippines — Mountain Province', + }, + { + key: 'PH:NEC', + label: 'Philippines — Negros Occidental', + }, + { + key: 'PH:NER', + label: 'Philippines — Negros Oriental', + }, + { + key: 'PH:NSA', + label: 'Philippines — Northern Samar', + }, + { + key: 'PH:NUE', + label: 'Philippines — Nueva Ecija', + }, + { + key: 'PH:NUV', + label: 'Philippines — Nueva Vizcaya', + }, + { + key: 'PH:MDC', + label: 'Philippines — Occidental Mindoro', + }, + { + key: 'PH:MDR', + label: 'Philippines — Oriental Mindoro', + }, + { + key: 'PH:PLW', + label: 'Philippines — Palawan', + }, + { + key: 'PH:PAM', + label: 'Philippines — Pampanga', + }, + { + key: 'PH:PAN', + label: 'Philippines — Pangasinan', + }, + { + key: 'PH:QUE', + label: 'Philippines — Quezon', + }, + { + key: 'PH:QUI', + label: 'Philippines — Quirino', + }, + { + key: 'PH:RIZ', + label: 'Philippines — Rizal', + }, + { + key: 'PH:ROM', + label: 'Philippines — Romblon', + }, + { + key: 'PH:WSA', + label: 'Philippines — Samar', + }, + { + key: 'PH:SAR', + label: 'Philippines — Sarangani', + }, + { + key: 'PH:SIQ', + label: 'Philippines — Siquijor', + }, + { + key: 'PH:SOR', + label: 'Philippines — Sorsogon', + }, + { + key: 'PH:SCO', + label: 'Philippines — South Cotabato', + }, + { + key: 'PH:SLE', + label: 'Philippines — Southern Leyte', + }, + { + key: 'PH:SUK', + label: 'Philippines — Sultan Kudarat', + }, + { + key: 'PH:SLU', + label: 'Philippines — Sulu', + }, + { + key: 'PH:SUN', + label: 'Philippines — Surigao del Norte', + }, + { + key: 'PH:SUR', + label: 'Philippines — Surigao del Sur', + }, + { + key: 'PH:TAR', + label: 'Philippines — Tarlac', + }, + { + key: 'PH:TAW', + label: 'Philippines — Tawi-Tawi', + }, + { + key: 'PH:ZMB', + label: 'Philippines — Zambales', + }, + { + key: 'PH:ZAN', + label: 'Philippines — Zamboanga del Norte', + }, + { + key: 'PH:ZAS', + label: 'Philippines — Zamboanga del Sur', + }, + { + key: 'PH:ZSI', + label: 'Philippines — Zamboanga Sibugay', + }, + { + key: 'PH:00', + label: 'Philippines — Metro Manila', + }, + { + key: 'PN', + label: 'Pitcairn', + }, + { + key: 'PL', + label: 'Poland', + }, + { + key: 'PT', + label: 'Portugal', + }, + { + key: 'PR', + label: 'Puerto Rico', + }, + { + key: 'QA', + label: 'Qatar', + }, + { + key: 'RE', + label: 'Reunion', + }, + { + key: 'RO:AB', + label: 'Romania — Alba', + }, + { + key: 'RO:AR', + label: 'Romania — Arad', + }, + { + key: 'RO:AG', + label: 'Romania — Argeș', + }, + { + key: 'RO:BC', + label: 'Romania — Bacău', + }, + { + key: 'RO:BH', + label: 'Romania — Bihor', + }, + { + key: 'RO:BN', + label: 'Romania — Bistrița-Năsăud', + }, + { + key: 'RO:BT', + label: 'Romania — Botoșani', + }, + { + key: 'RO:BR', + label: 'Romania — Brăila', + }, + { + key: 'RO:BV', + label: 'Romania — Brașov', + }, + { + key: 'RO:B', + label: 'Romania — București', + }, + { + key: 'RO:BZ', + label: 'Romania — Buzău', + }, + { + key: 'RO:CL', + label: 'Romania — Călărași', + }, + { + key: 'RO:CS', + label: 'Romania — Caraș-Severin', + }, + { + key: 'RO:CJ', + label: 'Romania — Cluj', + }, + { + key: 'RO:CT', + label: 'Romania — Constanța', + }, + { + key: 'RO:CV', + label: 'Romania — Covasna', + }, + { + key: 'RO:DB', + label: 'Romania — Dâmbovița', + }, + { + key: 'RO:DJ', + label: 'Romania — Dolj', + }, + { + key: 'RO:GL', + label: 'Romania — Galați', + }, + { + key: 'RO:GR', + label: 'Romania — Giurgiu', + }, + { + key: 'RO:GJ', + label: 'Romania — Gorj', + }, + { + key: 'RO:HR', + label: 'Romania — Harghita', + }, + { + key: 'RO:HD', + label: 'Romania — Hunedoara', + }, + { + key: 'RO:IL', + label: 'Romania — Ialomița', + }, + { + key: 'RO:IS', + label: 'Romania — Iași', + }, + { + key: 'RO:IF', + label: 'Romania — Ilfov', + }, + { + key: 'RO:MM', + label: 'Romania — Maramureș', + }, + { + key: 'RO:MH', + label: 'Romania — Mehedinți', + }, + { + key: 'RO:MS', + label: 'Romania — Mureș', + }, + { + key: 'RO:NT', + label: 'Romania — Neamț', + }, + { + key: 'RO:OT', + label: 'Romania — Olt', + }, + { + key: 'RO:PH', + label: 'Romania — Prahova', + }, + { + key: 'RO:SJ', + label: 'Romania — Sălaj', + }, + { + key: 'RO:SM', + label: 'Romania — Satu Mare', + }, + { + key: 'RO:SB', + label: 'Romania — Sibiu', + }, + { + key: 'RO:SV', + label: 'Romania — Suceava', + }, + { + key: 'RO:TR', + label: 'Romania — Teleorman', + }, + { + key: 'RO:TM', + label: 'Romania — Timiș', + }, + { + key: 'RO:TL', + label: 'Romania — Tulcea', + }, + { + key: 'RO:VL', + label: 'Romania — Vâlcea', + }, + { + key: 'RO:VS', + label: 'Romania — Vaslui', + }, + { + key: 'RO:VN', + label: 'Romania — Vrancea', + }, + { + key: 'RU', + label: 'Russia', + }, + { + key: 'RW', + label: 'Rwanda', + }, + { + key: 'ST', + label: 'São Tomé and Príncipe', + }, + { + key: 'BL', + label: 'Saint Barthélemy', + }, + { + key: 'SH', + label: 'Saint Helena', + }, + { + key: 'KN', + label: 'Saint Kitts and Nevis', + }, + { + key: 'LC', + label: 'Saint Lucia', + }, + { + key: 'SX', + label: 'Saint Martin (Dutch part)', + }, + { + key: 'MF', + label: 'Saint Martin (French part)', + }, + { + key: 'PM', + label: 'Saint Pierre and Miquelon', + }, + { + key: 'VC', + label: 'Saint Vincent and the Grenadines', + }, + { + key: 'WS', + label: 'Samoa', + }, + { + key: 'SM', + label: 'San Marino', + }, + { + key: 'SA', + label: 'Saudi Arabia', + }, + { + key: 'SN:SNDB', + label: 'Senegal — Diourbel', + }, + { + key: 'SN:SNDK', + label: 'Senegal — Dakar', + }, + { + key: 'SN:SNFK', + label: 'Senegal — Fatick', + }, + { + key: 'SN:SNKA', + label: 'Senegal — Kaffrine', + }, + { + key: 'SN:SNKD', + label: 'Senegal — Kolda', + }, + { + key: 'SN:SNKE', + label: 'Senegal — Kédougou', + }, + { + key: 'SN:SNKL', + label: 'Senegal — Kaolack', + }, + { + key: 'SN:SNLG', + label: 'Senegal — Louga', + }, + { + key: 'SN:SNMT', + label: 'Senegal — Matam', + }, + { + key: 'SN:SNSE', + label: 'Senegal — Sédhiou', + }, + { + key: 'SN:SNSL', + label: 'Senegal — Saint-Louis', + }, + { + key: 'SN:SNTC', + label: 'Senegal — Tambacounda', + }, + { + key: 'SN:SNTH', + label: 'Senegal — Thiès', + }, + { + key: 'SN:SNZG', + label: 'Senegal — Ziguinchor', + }, + { + key: 'RS:RS00', + label: 'Serbia — Belgrade', + }, + { + key: 'RS:RS14', + label: 'Serbia — Bor', + }, + { + key: 'RS:RS11', + label: 'Serbia — Braničevo', + }, + { + key: 'RS:RS02', + label: 'Serbia — Central Banat', + }, + { + key: 'RS:RS10', + label: 'Serbia — Danube', + }, + { + key: 'RS:RS23', + label: 'Serbia — Jablanica', + }, + { + key: 'RS:RS09', + label: 'Serbia — Kolubara', + }, + { + key: 'RS:RS08', + label: 'Serbia — Mačva', + }, + { + key: 'RS:RS17', + label: 'Serbia — Morava', + }, + { + key: 'RS:RS20', + label: 'Serbia — Nišava', + }, + { + key: 'RS:RS01', + label: 'Serbia — North Bačka', + }, + { + key: 'RS:RS03', + label: 'Serbia — North Banat', + }, + { + key: 'RS:RS24', + label: 'Serbia — Pčinja', + }, + { + key: 'RS:RS22', + label: 'Serbia — Pirot', + }, + { + key: 'RS:RS13', + label: 'Serbia — Pomoravlje', + }, + { + key: 'RS:RS19', + label: 'Serbia — Rasina', + }, + { + key: 'RS:RS18', + label: 'Serbia — Raška', + }, + { + key: 'RS:RS06', + label: 'Serbia — South Bačka', + }, + { + key: 'RS:RS04', + label: 'Serbia — South Banat', + }, + { + key: 'RS:RS07', + label: 'Serbia — Srem', + }, + { + key: 'RS:RS12', + label: 'Serbia — Šumadija', + }, + { + key: 'RS:RS21', + label: 'Serbia — Toplica', + }, + { + key: 'RS:RS05', + label: 'Serbia — West Bačka', + }, + { + key: 'RS:RS15', + label: 'Serbia — Zaječar', + }, + { + key: 'RS:RS16', + label: 'Serbia — Zlatibor', + }, + { + key: 'RS:RS25', + label: 'Serbia — Kosovo', + }, + { + key: 'RS:RS26', + label: 'Serbia — Peć', + }, + { + key: 'RS:RS27', + label: 'Serbia — Prizren', + }, + { + key: 'RS:RS28', + label: 'Serbia — Kosovska Mitrovica', + }, + { + key: 'RS:RS29', + label: 'Serbia — Kosovo-Pomoravlje', + }, + { + key: 'RS:RSKM', + label: 'Serbia — Kosovo-Metohija', + }, + { + key: 'RS:RSVO', + label: 'Serbia — Vojvodina', + }, + { + key: 'SC', + label: 'Seychelles', + }, + { + key: 'SL', + label: 'Sierra Leone', + }, + { + key: 'SG', + label: 'Singapore', + }, + { + key: 'SK', + label: 'Slovakia', + }, + { + key: 'SI', + label: 'Slovenia', + }, + { + key: 'SB', + label: 'Solomon Islands', + }, + { + key: 'SO', + label: 'Somalia', + }, + { + key: 'ZA:EC', + label: 'South Africa — Eastern Cape', + }, + { + key: 'ZA:FS', + label: 'South Africa — Free State', + }, + { + key: 'ZA:GP', + label: 'South Africa — Gauteng', + }, + { + key: 'ZA:KZN', + label: 'South Africa — KwaZulu-Natal', + }, + { + key: 'ZA:LP', + label: 'South Africa — Limpopo', + }, + { + key: 'ZA:MP', + label: 'South Africa — Mpumalanga', + }, + { + key: 'ZA:NC', + label: 'South Africa — Northern Cape', + }, + { + key: 'ZA:NW', + label: 'South Africa — North West', + }, + { + key: 'ZA:WC', + label: 'South Africa — Western Cape', + }, + { + key: 'GS', + label: 'South Georgia/Sandwich Islands', + }, + { + key: 'KR', + label: 'South Korea', + }, + { + key: 'SS', + label: 'South Sudan', + }, + { + key: 'ES:C', + label: 'Spain — A Coruña', + }, + { + key: 'ES:VI', + label: 'Spain — Araba/Álava', + }, + { + key: 'ES:AB', + label: 'Spain — Albacete', + }, + { + key: 'ES:A', + label: 'Spain — Alicante', + }, + { + key: 'ES:AL', + label: 'Spain — Almería', + }, + { + key: 'ES:O', + label: 'Spain — Asturias', + }, + { + key: 'ES:AV', + label: 'Spain — Ávila', + }, + { + key: 'ES:BA', + label: 'Spain — Badajoz', + }, + { + key: 'ES:PM', + label: 'Spain — Baleares', + }, + { + key: 'ES:B', + label: 'Spain — Barcelona', + }, + { + key: 'ES:BU', + label: 'Spain — Burgos', + }, + { + key: 'ES:CC', + label: 'Spain — Cáceres', + }, + { + key: 'ES:CA', + label: 'Spain — Cádiz', + }, + { + key: 'ES:S', + label: 'Spain — Cantabria', + }, + { + key: 'ES:CS', + label: 'Spain — Castellón', + }, + { + key: 'ES:CE', + label: 'Spain — Ceuta', + }, + { + key: 'ES:CR', + label: 'Spain — Ciudad Real', + }, + { + key: 'ES:CO', + label: 'Spain — Córdoba', + }, + { + key: 'ES:CU', + label: 'Spain — Cuenca', + }, + { + key: 'ES:GI', + label: 'Spain — Girona', + }, + { + key: 'ES:GR', + label: 'Spain — Granada', + }, + { + key: 'ES:GU', + label: 'Spain — Guadalajara', + }, + { + key: 'ES:SS', + label: 'Spain — Gipuzkoa', + }, + { + key: 'ES:H', + label: 'Spain — Huelva', + }, + { + key: 'ES:HU', + label: 'Spain — Huesca', + }, + { + key: 'ES:J', + label: 'Spain — Jaén', + }, + { + key: 'ES:LO', + label: 'Spain — La Rioja', + }, + { + key: 'ES:GC', + label: 'Spain — Las Palmas', + }, + { + key: 'ES:LE', + label: 'Spain — León', + }, + { + key: 'ES:L', + label: 'Spain — Lleida', + }, + { + key: 'ES:LU', + label: 'Spain — Lugo', + }, + { + key: 'ES:M', + label: 'Spain — Madrid', + }, + { + key: 'ES:MA', + label: 'Spain — Málaga', + }, + { + key: 'ES:ML', + label: 'Spain — Melilla', + }, + { + key: 'ES:MU', + label: 'Spain — Murcia', + }, + { + key: 'ES:NA', + label: 'Spain — Navarra', + }, + { + key: 'ES:OR', + label: 'Spain — Ourense', + }, + { + key: 'ES:P', + label: 'Spain — Palencia', + }, + { + key: 'ES:PO', + label: 'Spain — Pontevedra', + }, + { + key: 'ES:SA', + label: 'Spain — Salamanca', + }, + { + key: 'ES:TF', + label: 'Spain — Santa Cruz de Tenerife', + }, + { + key: 'ES:SG', + label: 'Spain — Segovia', + }, + { + key: 'ES:SE', + label: 'Spain — Sevilla', + }, + { + key: 'ES:SO', + label: 'Spain — Soria', + }, + { + key: 'ES:T', + label: 'Spain — Tarragona', + }, + { + key: 'ES:TE', + label: 'Spain — Teruel', + }, + { + key: 'ES:TO', + label: 'Spain — Toledo', + }, + { + key: 'ES:V', + label: 'Spain — Valencia', + }, + { + key: 'ES:VA', + label: 'Spain — Valladolid', + }, + { + key: 'ES:BI', + label: 'Spain — Biscay', + }, + { + key: 'ES:ZA', + label: 'Spain — Zamora', + }, + { + key: 'ES:Z', + label: 'Spain — Zaragoza', + }, + { + key: 'LK', + label: 'Sri Lanka', + }, + { + key: 'SD', + label: 'Sudan', + }, + { + key: 'SR', + label: 'Suriname', + }, + { + key: 'SJ', + label: 'Svalbard and Jan Mayen', + }, + { + key: 'SE', + label: 'Sweden', + }, + { + key: 'CH:AG', + label: 'Switzerland — Aargau', + }, + { + key: 'CH:AR', + label: 'Switzerland — Appenzell Ausserrhoden', + }, + { + key: 'CH:AI', + label: 'Switzerland — Appenzell Innerrhoden', + }, + { + key: 'CH:BL', + label: 'Switzerland — Basel-Landschaft', + }, + { + key: 'CH:BS', + label: 'Switzerland — Basel-Stadt', + }, + { + key: 'CH:BE', + label: 'Switzerland — Bern', + }, + { + key: 'CH:FR', + label: 'Switzerland — Fribourg', + }, + { + key: 'CH:GE', + label: 'Switzerland — Geneva', + }, + { + key: 'CH:GL', + label: 'Switzerland — Glarus', + }, + { + key: 'CH:GR', + label: 'Switzerland — Graubünden', + }, + { + key: 'CH:JU', + label: 'Switzerland — Jura', + }, + { + key: 'CH:LU', + label: 'Switzerland — Luzern', + }, + { + key: 'CH:NE', + label: 'Switzerland — Neuchâtel', + }, + { + key: 'CH:NW', + label: 'Switzerland — Nidwalden', + }, + { + key: 'CH:OW', + label: 'Switzerland — Obwalden', + }, + { + key: 'CH:SH', + label: 'Switzerland — Schaffhausen', + }, + { + key: 'CH:SZ', + label: 'Switzerland — Schwyz', + }, + { + key: 'CH:SO', + label: 'Switzerland — Solothurn', + }, + { + key: 'CH:SG', + label: 'Switzerland — St. Gallen', + }, + { + key: 'CH:TG', + label: 'Switzerland — Thurgau', + }, + { + key: 'CH:TI', + label: 'Switzerland — Ticino', + }, + { + key: 'CH:UR', + label: 'Switzerland — Uri', + }, + { + key: 'CH:VS', + label: 'Switzerland — Valais', + }, + { + key: 'CH:VD', + label: 'Switzerland — Vaud', + }, + { + key: 'CH:ZG', + label: 'Switzerland — Zug', + }, + { + key: 'CH:ZH', + label: 'Switzerland — Zürich', + }, + { + key: 'SY', + label: 'Syria', + }, + { + key: 'TW', + label: 'Taiwan', + }, + { + key: 'TJ', + label: 'Tajikistan', + }, + { + key: 'TZ:TZ01', + label: 'Tanzania — Arusha', + }, + { + key: 'TZ:TZ02', + label: 'Tanzania — Dar es Salaam', + }, + { + key: 'TZ:TZ03', + label: 'Tanzania — Dodoma', + }, + { + key: 'TZ:TZ04', + label: 'Tanzania — Iringa', + }, + { + key: 'TZ:TZ05', + label: 'Tanzania — Kagera', + }, + { + key: 'TZ:TZ06', + label: 'Tanzania — Pemba North', + }, + { + key: 'TZ:TZ07', + label: 'Tanzania — Zanzibar North', + }, + { + key: 'TZ:TZ08', + label: 'Tanzania — Kigoma', + }, + { + key: 'TZ:TZ09', + label: 'Tanzania — Kilimanjaro', + }, + { + key: 'TZ:TZ10', + label: 'Tanzania — Pemba South', + }, + { + key: 'TZ:TZ11', + label: 'Tanzania — Zanzibar South', + }, + { + key: 'TZ:TZ12', + label: 'Tanzania — Lindi', + }, + { + key: 'TZ:TZ13', + label: 'Tanzania — Mara', + }, + { + key: 'TZ:TZ14', + label: 'Tanzania — Mbeya', + }, + { + key: 'TZ:TZ15', + label: 'Tanzania — Zanzibar West', + }, + { + key: 'TZ:TZ16', + label: 'Tanzania — Morogoro', + }, + { + key: 'TZ:TZ17', + label: 'Tanzania — Mtwara', + }, + { + key: 'TZ:TZ18', + label: 'Tanzania — Mwanza', + }, + { + key: 'TZ:TZ19', + label: 'Tanzania — Coast', + }, + { + key: 'TZ:TZ20', + label: 'Tanzania — Rukwa', + }, + { + key: 'TZ:TZ21', + label: 'Tanzania — Ruvuma', + }, + { + key: 'TZ:TZ22', + label: 'Tanzania — Shinyanga', + }, + { + key: 'TZ:TZ23', + label: 'Tanzania — Singida', + }, + { + key: 'TZ:TZ24', + label: 'Tanzania — Tabora', + }, + { + key: 'TZ:TZ25', + label: 'Tanzania — Tanga', + }, + { + key: 'TZ:TZ26', + label: 'Tanzania — Manyara', + }, + { + key: 'TZ:TZ27', + label: 'Tanzania — Geita', + }, + { + key: 'TZ:TZ28', + label: 'Tanzania — Katavi', + }, + { + key: 'TZ:TZ29', + label: 'Tanzania — Njombe', + }, + { + key: 'TZ:TZ30', + label: 'Tanzania — Simiyu', + }, + { + key: 'TH:TH-37', + label: 'Thailand — Amnat Charoen', + }, + { + key: 'TH:TH-15', + label: 'Thailand — Ang Thong', + }, + { + key: 'TH:TH-14', + label: 'Thailand — Ayutthaya', + }, + { + key: 'TH:TH-10', + label: 'Thailand — Bangkok', + }, + { + key: 'TH:TH-38', + label: 'Thailand — Bueng Kan', + }, + { + key: 'TH:TH-31', + label: 'Thailand — Buri Ram', + }, + { + key: 'TH:TH-24', + label: 'Thailand — Chachoengsao', + }, + { + key: 'TH:TH-18', + label: 'Thailand — Chai Nat', + }, + { + key: 'TH:TH-36', + label: 'Thailand — Chaiyaphum', + }, + { + key: 'TH:TH-22', + label: 'Thailand — Chanthaburi', + }, + { + key: 'TH:TH-50', + label: 'Thailand — Chiang Mai', + }, + { + key: 'TH:TH-57', + label: 'Thailand — Chiang Rai', + }, + { + key: 'TH:TH-20', + label: 'Thailand — Chonburi', + }, + { + key: 'TH:TH-86', + label: 'Thailand — Chumphon', + }, + { + key: 'TH:TH-46', + label: 'Thailand — Kalasin', + }, + { + key: 'TH:TH-62', + label: 'Thailand — Kamphaeng Phet', + }, + { + key: 'TH:TH-71', + label: 'Thailand — Kanchanaburi', + }, + { + key: 'TH:TH-40', + label: 'Thailand — Khon Kaen', + }, + { + key: 'TH:TH-81', + label: 'Thailand — Krabi', + }, + { + key: 'TH:TH-52', + label: 'Thailand — Lampang', + }, + { + key: 'TH:TH-51', + label: 'Thailand — Lamphun', + }, + { + key: 'TH:TH-42', + label: 'Thailand — Loei', + }, + { + key: 'TH:TH-16', + label: 'Thailand — Lopburi', + }, + { + key: 'TH:TH-58', + label: 'Thailand — Mae Hong Son', + }, + { + key: 'TH:TH-44', + label: 'Thailand — Maha Sarakham', + }, + { + key: 'TH:TH-49', + label: 'Thailand — Mukdahan', + }, + { + key: 'TH:TH-26', + label: 'Thailand — Nakhon Nayok', + }, + { + key: 'TH:TH-73', + label: 'Thailand — Nakhon Pathom', + }, + { + key: 'TH:TH-48', + label: 'Thailand — Nakhon Phanom', + }, + { + key: 'TH:TH-30', + label: 'Thailand — Nakhon Ratchasima', + }, + { + key: 'TH:TH-60', + label: 'Thailand — Nakhon Sawan', + }, + { + key: 'TH:TH-80', + label: 'Thailand — Nakhon Si Thammarat', + }, + { + key: 'TH:TH-55', + label: 'Thailand — Nan', + }, + { + key: 'TH:TH-96', + label: 'Thailand — Narathiwat', + }, + { + key: 'TH:TH-39', + label: 'Thailand — Nong Bua Lam Phu', + }, + { + key: 'TH:TH-43', + label: 'Thailand — Nong Khai', + }, + { + key: 'TH:TH-12', + label: 'Thailand — Nonthaburi', + }, + { + key: 'TH:TH-13', + label: 'Thailand — Pathum Thani', + }, + { + key: 'TH:TH-94', + label: 'Thailand — Pattani', + }, + { + key: 'TH:TH-82', + label: 'Thailand — Phang Nga', + }, + { + key: 'TH:TH-93', + label: 'Thailand — Phatthalung', + }, + { + key: 'TH:TH-56', + label: 'Thailand — Phayao', + }, + { + key: 'TH:TH-67', + label: 'Thailand — Phetchabun', + }, + { + key: 'TH:TH-76', + label: 'Thailand — Phetchaburi', + }, + { + key: 'TH:TH-66', + label: 'Thailand — Phichit', + }, + { + key: 'TH:TH-65', + label: 'Thailand — Phitsanulok', + }, + { + key: 'TH:TH-54', + label: 'Thailand — Phrae', + }, + { + key: 'TH:TH-83', + label: 'Thailand — Phuket', + }, + { + key: 'TH:TH-25', + label: 'Thailand — Prachin Buri', + }, + { + key: 'TH:TH-77', + label: 'Thailand — Prachuap Khiri Khan', + }, + { + key: 'TH:TH-85', + label: 'Thailand — Ranong', + }, + { + key: 'TH:TH-70', + label: 'Thailand — Ratchaburi', + }, + { + key: 'TH:TH-21', + label: 'Thailand — Rayong', + }, + { + key: 'TH:TH-45', + label: 'Thailand — Roi Et', + }, + { + key: 'TH:TH-27', + label: 'Thailand — Sa Kaeo', + }, + { + key: 'TH:TH-47', + label: 'Thailand — Sakon Nakhon', + }, + { + key: 'TH:TH-11', + label: 'Thailand — Samut Prakan', + }, + { + key: 'TH:TH-74', + label: 'Thailand — Samut Sakhon', + }, + { + key: 'TH:TH-75', + label: 'Thailand — Samut Songkhram', + }, + { + key: 'TH:TH-19', + label: 'Thailand — Saraburi', + }, + { + key: 'TH:TH-91', + label: 'Thailand — Satun', + }, + { + key: 'TH:TH-17', + label: 'Thailand — Sing Buri', + }, + { + key: 'TH:TH-33', + label: 'Thailand — Sisaket', + }, + { + key: 'TH:TH-90', + label: 'Thailand — Songkhla', + }, + { + key: 'TH:TH-64', + label: 'Thailand — Sukhothai', + }, + { + key: 'TH:TH-72', + label: 'Thailand — Suphan Buri', + }, + { + key: 'TH:TH-84', + label: 'Thailand — Surat Thani', + }, + { + key: 'TH:TH-32', + label: 'Thailand — Surin', + }, + { + key: 'TH:TH-63', + label: 'Thailand — Tak', + }, + { + key: 'TH:TH-92', + label: 'Thailand — Trang', + }, + { + key: 'TH:TH-23', + label: 'Thailand — Trat', + }, + { + key: 'TH:TH-34', + label: 'Thailand — Ubon Ratchathani', + }, + { + key: 'TH:TH-41', + label: 'Thailand — Udon Thani', + }, + { + key: 'TH:TH-61', + label: 'Thailand — Uthai Thani', + }, + { + key: 'TH:TH-53', + label: 'Thailand — Uttaradit', + }, + { + key: 'TH:TH-95', + label: 'Thailand — Yala', + }, + { + key: 'TH:TH-35', + label: 'Thailand — Yasothon', + }, + { + key: 'TL', + label: 'Timor-Leste', + }, + { + key: 'TG', + label: 'Togo', + }, + { + key: 'TK', + label: 'Tokelau', + }, + { + key: 'TO', + label: 'Tonga', + }, + { + key: 'TT', + label: 'Trinidad and Tobago', + }, + { + key: 'TN', + label: 'Tunisia', + }, + { + key: 'TR:TR01', + label: 'Turkey — Adana', + }, + { + key: 'TR:TR02', + label: 'Turkey — Adıyaman', + }, + { + key: 'TR:TR03', + label: 'Turkey — Afyon', + }, + { + key: 'TR:TR04', + label: 'Turkey — Ağrı', + }, + { + key: 'TR:TR05', + label: 'Turkey — Amasya', + }, + { + key: 'TR:TR06', + label: 'Turkey — Ankara', + }, + { + key: 'TR:TR07', + label: 'Turkey — Antalya', + }, + { + key: 'TR:TR08', + label: 'Turkey — Artvin', + }, + { + key: 'TR:TR09', + label: 'Turkey — Aydın', + }, + { + key: 'TR:TR10', + label: 'Turkey — Balıkesir', + }, + { + key: 'TR:TR11', + label: 'Turkey — Bilecik', + }, + { + key: 'TR:TR12', + label: 'Turkey — Bingöl', + }, + { + key: 'TR:TR13', + label: 'Turkey — Bitlis', + }, + { + key: 'TR:TR14', + label: 'Turkey — Bolu', + }, + { + key: 'TR:TR15', + label: 'Turkey — Burdur', + }, + { + key: 'TR:TR16', + label: 'Turkey — Bursa', + }, + { + key: 'TR:TR17', + label: 'Turkey — Çanakkale', + }, + { + key: 'TR:TR18', + label: 'Turkey — Çankırı', + }, + { + key: 'TR:TR19', + label: 'Turkey — Çorum', + }, + { + key: 'TR:TR20', + label: 'Turkey — Denizli', + }, + { + key: 'TR:TR21', + label: 'Turkey — Diyarbakır', + }, + { + key: 'TR:TR22', + label: 'Turkey — Edirne', + }, + { + key: 'TR:TR23', + label: 'Turkey — Elazığ', + }, + { + key: 'TR:TR24', + label: 'Turkey — Erzincan', + }, + { + key: 'TR:TR25', + label: 'Turkey — Erzurum', + }, + { + key: 'TR:TR26', + label: 'Turkey — Eskişehir', + }, + { + key: 'TR:TR27', + label: 'Turkey — Gaziantep', + }, + { + key: 'TR:TR28', + label: 'Turkey — Giresun', + }, + { + key: 'TR:TR29', + label: 'Turkey — Gümüşhane', + }, + { + key: 'TR:TR30', + label: 'Turkey — Hakkari', + }, + { + key: 'TR:TR31', + label: 'Turkey — Hatay', + }, + { + key: 'TR:TR32', + label: 'Turkey — Isparta', + }, + { + key: 'TR:TR33', + label: 'Turkey — İçel', + }, + { + key: 'TR:TR34', + label: 'Turkey — İstanbul', + }, + { + key: 'TR:TR35', + label: 'Turkey — İzmir', + }, + { + key: 'TR:TR36', + label: 'Turkey — Kars', + }, + { + key: 'TR:TR37', + label: 'Turkey — Kastamonu', + }, + { + key: 'TR:TR38', + label: 'Turkey — Kayseri', + }, + { + key: 'TR:TR39', + label: 'Turkey — Kırklareli', + }, + { + key: 'TR:TR40', + label: 'Turkey — Kırşehir', + }, + { + key: 'TR:TR41', + label: 'Turkey — Kocaeli', + }, + { + key: 'TR:TR42', + label: 'Turkey — Konya', + }, + { + key: 'TR:TR43', + label: 'Turkey — Kütahya', + }, + { + key: 'TR:TR44', + label: 'Turkey — Malatya', + }, + { + key: 'TR:TR45', + label: 'Turkey — Manisa', + }, + { + key: 'TR:TR46', + label: 'Turkey — Kahramanmaraş', + }, + { + key: 'TR:TR47', + label: 'Turkey — Mardin', + }, + { + key: 'TR:TR48', + label: 'Turkey — Muğla', + }, + { + key: 'TR:TR49', + label: 'Turkey — Muş', + }, + { + key: 'TR:TR50', + label: 'Turkey — Nevşehir', + }, + { + key: 'TR:TR51', + label: 'Turkey — Niğde', + }, + { + key: 'TR:TR52', + label: 'Turkey — Ordu', + }, + { + key: 'TR:TR53', + label: 'Turkey — Rize', + }, + { + key: 'TR:TR54', + label: 'Turkey — Sakarya', + }, + { + key: 'TR:TR55', + label: 'Turkey — Samsun', + }, + { + key: 'TR:TR56', + label: 'Turkey — Siirt', + }, + { + key: 'TR:TR57', + label: 'Turkey — Sinop', + }, + { + key: 'TR:TR58', + label: 'Turkey — Sivas', + }, + { + key: 'TR:TR59', + label: 'Turkey — Tekirdağ', + }, + { + key: 'TR:TR60', + label: 'Turkey — Tokat', + }, + { + key: 'TR:TR61', + label: 'Turkey — Trabzon', + }, + { + key: 'TR:TR62', + label: 'Turkey — Tunceli', + }, + { + key: 'TR:TR63', + label: 'Turkey — Şanlıurfa', + }, + { + key: 'TR:TR64', + label: 'Turkey — Uşak', + }, + { + key: 'TR:TR65', + label: 'Turkey — Van', + }, + { + key: 'TR:TR66', + label: 'Turkey — Yozgat', + }, + { + key: 'TR:TR67', + label: 'Turkey — Zonguldak', + }, + { + key: 'TR:TR68', + label: 'Turkey — Aksaray', + }, + { + key: 'TR:TR69', + label: 'Turkey — Bayburt', + }, + { + key: 'TR:TR70', + label: 'Turkey — Karaman', + }, + { + key: 'TR:TR71', + label: 'Turkey — Kırıkkale', + }, + { + key: 'TR:TR72', + label: 'Turkey — Batman', + }, + { + key: 'TR:TR73', + label: 'Turkey — Şırnak', + }, + { + key: 'TR:TR74', + label: 'Turkey — Bartın', + }, + { + key: 'TR:TR75', + label: 'Turkey — Ardahan', + }, + { + key: 'TR:TR76', + label: 'Turkey — Iğdır', + }, + { + key: 'TR:TR77', + label: 'Turkey — Yalova', + }, + { + key: 'TR:TR78', + label: 'Turkey — Karabük', + }, + { + key: 'TR:TR79', + label: 'Turkey — Kilis', + }, + { + key: 'TR:TR80', + label: 'Turkey — Osmaniye', + }, + { + key: 'TR:TR81', + label: 'Turkey — Düzce', + }, + { + key: 'TM', + label: 'Turkmenistan', + }, + { + key: 'TC', + label: 'Turks and Caicos Islands', + }, + { + key: 'TV', + label: 'Tuvalu', + }, + { + key: 'UG:UG314', + label: 'Uganda — Abim', + }, + { + key: 'UG:UG301', + label: 'Uganda — Adjumani', + }, + { + key: 'UG:UG322', + label: 'Uganda — Agago', + }, + { + key: 'UG:UG323', + label: 'Uganda — Alebtong', + }, + { + key: 'UG:UG315', + label: 'Uganda — Amolatar', + }, + { + key: 'UG:UG324', + label: 'Uganda — Amudat', + }, + { + key: 'UG:UG216', + label: 'Uganda — Amuria', + }, + { + key: 'UG:UG316', + label: 'Uganda — Amuru', + }, + { + key: 'UG:UG302', + label: 'Uganda — Apac', + }, + { + key: 'UG:UG303', + label: 'Uganda — Arua', + }, + { + key: 'UG:UG217', + label: 'Uganda — Budaka', + }, + { + key: 'UG:UG218', + label: 'Uganda — Bududa', + }, + { + key: 'UG:UG201', + label: 'Uganda — Bugiri', + }, + { + key: 'UG:UG235', + label: 'Uganda — Bugweri', + }, + { + key: 'UG:UG420', + label: 'Uganda — Buhweju', + }, + { + key: 'UG:UG117', + label: 'Uganda — Buikwe', + }, + { + key: 'UG:UG219', + label: 'Uganda — Bukedea', + }, + { + key: 'UG:UG118', + label: 'Uganda — Bukomansimbi', + }, + { + key: 'UG:UG220', + label: 'Uganda — Bukwa', + }, + { + key: 'UG:UG225', + label: 'Uganda — Bulambuli', + }, + { + key: 'UG:UG416', + label: 'Uganda — Buliisa', + }, + { + key: 'UG:UG401', + label: 'Uganda — Bundibugyo', + }, + { + key: 'UG:UG430', + label: 'Uganda — Bunyangabu', + }, + { + key: 'UG:UG402', + label: 'Uganda — Bushenyi', + }, + { + key: 'UG:UG202', + label: 'Uganda — Busia', + }, + { + key: 'UG:UG221', + label: 'Uganda — Butaleja', + }, + { + key: 'UG:UG119', + label: 'Uganda — Butambala', + }, + { + key: 'UG:UG233', + label: 'Uganda — Butebo', + }, + { + key: 'UG:UG120', + label: 'Uganda — Buvuma', + }, + { + key: 'UG:UG226', + label: 'Uganda — Buyende', + }, + { + key: 'UG:UG317', + label: 'Uganda — Dokolo', + }, + { + key: 'UG:UG121', + label: 'Uganda — Gomba', + }, + { + key: 'UG:UG304', + label: 'Uganda — Gulu', + }, + { + key: 'UG:UG403', + label: 'Uganda — Hoima', + }, + { + key: 'UG:UG417', + label: 'Uganda — Ibanda', + }, + { + key: 'UG:UG203', + label: 'Uganda — Iganga', + }, + { + key: 'UG:UG418', + label: 'Uganda — Isingiro', + }, + { + key: 'UG:UG204', + label: 'Uganda — Jinja', + }, + { + key: 'UG:UG318', + label: 'Uganda — Kaabong', + }, + { + key: 'UG:UG404', + label: 'Uganda — Kabale', + }, + { + key: 'UG:UG405', + label: 'Uganda — Kabarole', + }, + { + key: 'UG:UG213', + label: 'Uganda — Kaberamaido', + }, + { + key: 'UG:UG427', + label: 'Uganda — Kagadi', + }, + { + key: 'UG:UG428', + label: 'Uganda — Kakumiro', + }, + { + key: 'UG:UG101', + label: 'Uganda — Kalangala', + }, + { + key: 'UG:UG222', + label: 'Uganda — Kaliro', + }, + { + key: 'UG:UG122', + label: 'Uganda — Kalungu', + }, + { + key: 'UG:UG102', + label: 'Uganda — Kampala', + }, + { + key: 'UG:UG205', + label: 'Uganda — Kamuli', + }, + { + key: 'UG:UG413', + label: 'Uganda — Kamwenge', + }, + { + key: 'UG:UG414', + label: 'Uganda — Kanungu', + }, + { + key: 'UG:UG206', + label: 'Uganda — Kapchorwa', + }, + { + key: 'UG:UG236', + label: 'Uganda — Kapelebyong', + }, + { + key: 'UG:UG126', + label: 'Uganda — Kasanda', + }, + { + key: 'UG:UG406', + label: 'Uganda — Kasese', + }, + { + key: 'UG:UG207', + label: 'Uganda — Katakwi', + }, + { + key: 'UG:UG112', + label: 'Uganda — Kayunga', + }, + { + key: 'UG:UG407', + label: 'Uganda — Kibaale', + }, + { + key: 'UG:UG103', + label: 'Uganda — Kiboga', + }, + { + key: 'UG:UG227', + label: 'Uganda — Kibuku', + }, + { + key: 'UG:UG432', + label: 'Uganda — Kikuube', + }, + { + key: 'UG:UG419', + label: 'Uganda — Kiruhura', + }, + { + key: 'UG:UG421', + label: 'Uganda — Kiryandongo', + }, + { + key: 'UG:UG408', + label: 'Uganda — Kisoro', + }, + { + key: 'UG:UG305', + label: 'Uganda — Kitgum', + }, + { + key: 'UG:UG319', + label: 'Uganda — Koboko', + }, + { + key: 'UG:UG325', + label: 'Uganda — Kole', + }, + { + key: 'UG:UG306', + label: 'Uganda — Kotido', + }, + { + key: 'UG:UG208', + label: 'Uganda — Kumi', + }, + { + key: 'UG:UG333', + label: 'Uganda — Kwania', + }, + { + key: 'UG:UG228', + label: 'Uganda — Kween', + }, + { + key: 'UG:UG123', + label: 'Uganda — Kyankwanzi', + }, + { + key: 'UG:UG422', + label: 'Uganda — Kyegegwa', + }, + { + key: 'UG:UG415', + label: 'Uganda — Kyenjojo', + }, + { + key: 'UG:UG125', + label: 'Uganda — Kyotera', + }, + { + key: 'UG:UG326', + label: 'Uganda — Lamwo', + }, + { + key: 'UG:UG307', + label: 'Uganda — Lira', + }, + { + key: 'UG:UG229', + label: 'Uganda — Luuka', + }, + { + key: 'UG:UG104', + label: 'Uganda — Luwero', + }, + { + key: 'UG:UG124', + label: 'Uganda — Lwengo', + }, + { + key: 'UG:UG114', + label: 'Uganda — Lyantonde', + }, + { + key: 'UG:UG223', + label: 'Uganda — Manafwa', + }, + { + key: 'UG:UG320', + label: 'Uganda — Maracha', + }, + { + key: 'UG:UG105', + label: 'Uganda — Masaka', + }, + { + key: 'UG:UG409', + label: 'Uganda — Masindi', + }, + { + key: 'UG:UG214', + label: 'Uganda — Mayuge', + }, + { + key: 'UG:UG209', + label: 'Uganda — Mbale', + }, + { + key: 'UG:UG410', + label: 'Uganda — Mbarara', + }, + { + key: 'UG:UG423', + label: 'Uganda — Mitooma', + }, + { + key: 'UG:UG115', + label: 'Uganda — Mityana', + }, + { + key: 'UG:UG308', + label: 'Uganda — Moroto', + }, + { + key: 'UG:UG309', + label: 'Uganda — Moyo', + }, + { + key: 'UG:UG106', + label: 'Uganda — Mpigi', + }, + { + key: 'UG:UG107', + label: 'Uganda — Mubende', + }, + { + key: 'UG:UG108', + label: 'Uganda — Mukono', + }, + { + key: 'UG:UG334', + label: 'Uganda — Nabilatuk', + }, + { + key: 'UG:UG311', + label: 'Uganda — Nakapiripirit', + }, + { + key: 'UG:UG116', + label: 'Uganda — Nakaseke', + }, + { + key: 'UG:UG109', + label: 'Uganda — Nakasongola', + }, + { + key: 'UG:UG230', + label: 'Uganda — Namayingo', + }, + { + key: 'UG:UG234', + label: 'Uganda — Namisindwa', + }, + { + key: 'UG:UG224', + label: 'Uganda — Namutumba', + }, + { + key: 'UG:UG327', + label: 'Uganda — Napak', + }, + { + key: 'UG:UG310', + label: 'Uganda — Nebbi', + }, + { + key: 'UG:UG231', + label: 'Uganda — Ngora', + }, + { + key: 'UG:UG424', + label: 'Uganda — Ntoroko', + }, + { + key: 'UG:UG411', + label: 'Uganda — Ntungamo', + }, + { + key: 'UG:UG328', + label: 'Uganda — Nwoya', + }, + { + key: 'UG:UG331', + label: 'Uganda — Omoro', + }, + { + key: 'UG:UG329', + label: 'Uganda — Otuke', + }, + { + key: 'UG:UG321', + label: 'Uganda — Oyam', + }, + { + key: 'UG:UG312', + label: 'Uganda — Pader', + }, + { + key: 'UG:UG332', + label: 'Uganda — Pakwach', + }, + { + key: 'UG:UG210', + label: 'Uganda — Pallisa', + }, + { + key: 'UG:UG110', + label: 'Uganda — Rakai', + }, + { + key: 'UG:UG429', + label: 'Uganda — Rubanda', + }, + { + key: 'UG:UG425', + label: 'Uganda — Rubirizi', + }, + { + key: 'UG:UG431', + label: 'Uganda — Rukiga', + }, + { + key: 'UG:UG412', + label: 'Uganda — Rukungiri', + }, + { + key: 'UG:UG111', + label: 'Uganda — Sembabule', + }, + { + key: 'UG:UG232', + label: 'Uganda — Serere', + }, + { + key: 'UG:UG426', + label: 'Uganda — Sheema', + }, + { + key: 'UG:UG215', + label: 'Uganda — Sironko', + }, + { + key: 'UG:UG211', + label: 'Uganda — Soroti', + }, + { + key: 'UG:UG212', + label: 'Uganda — Tororo', + }, + { + key: 'UG:UG113', + label: 'Uganda — Wakiso', + }, + { + key: 'UG:UG313', + label: 'Uganda — Yumbe', + }, + { + key: 'UG:UG330', + label: 'Uganda — Zombo', + }, + { + key: 'UA:VN', + label: 'Ukraine — Vinnytsia Oblast', + }, + { + key: 'UA:VL', + label: 'Ukraine — Volyn Oblast', + }, + { + key: 'UA:DP', + label: 'Ukraine — Dnipropetrovsk Oblast', + }, + { + key: 'UA:DT', + label: 'Ukraine — Donetsk Oblast', + }, + { + key: 'UA:ZT', + label: 'Ukraine — Zhytomyr Oblast', + }, + { + key: 'UA:ZK', + label: 'Ukraine — Zakarpattia Oblast', + }, + { + key: 'UA:ZP', + label: 'Ukraine — Zaporizhzhia Oblast', + }, + { + key: 'UA:IF', + label: 'Ukraine — Ivano-Frankivsk Oblast', + }, + { + key: 'UA:KV', + label: 'Ukraine — Kyiv Oblast', + }, + { + key: 'UA:KH', + label: 'Ukraine — Kirovohrad Oblast', + }, + { + key: 'UA:LH', + label: 'Ukraine — Luhansk Oblast', + }, + { + key: 'UA:LV', + label: 'Ukraine — Lviv Oblast', + }, + { + key: 'UA:MY', + label: 'Ukraine — Mykolaiv Oblast', + }, + { + key: 'UA:OD', + label: 'Ukraine — Odessa Oblast', + }, + { + key: 'UA:PL', + label: 'Ukraine — Poltava Oblast', + }, + { + key: 'UA:RV', + label: 'Ukraine — Rivne Oblast', + }, + { + key: 'UA:SM', + label: 'Ukraine — Sumy Oblast', + }, + { + key: 'UA:TP', + label: 'Ukraine — Ternopil Oblast', + }, + { + key: 'UA:KK', + label: 'Ukraine — Kharkiv Oblast', + }, + { + key: 'UA:KS', + label: 'Ukraine — Kherson Oblast', + }, + { + key: 'UA:KM', + label: 'Ukraine — Khmelnytskyi Oblast', + }, + { + key: 'UA:CK', + label: 'Ukraine — Cherkasy Oblast', + }, + { + key: 'UA:CH', + label: 'Ukraine — Chernihiv Oblast', + }, + { + key: 'UA:CV', + label: 'Ukraine — Chernivtsi Oblast', + }, + { + key: 'AE', + label: 'United Arab Emirates', + }, + { + key: 'GB', + label: 'United Kingdom (UK)', + }, + { + key: 'US:AL', + label: 'United States (US) — Alabama', + }, + { + key: 'US:AK', + label: 'United States (US) — Alaska', + }, + { + key: 'US:AZ', + label: 'United States (US) — Arizona', + }, + { + key: 'US:AR', + label: 'United States (US) — Arkansas', + }, + { + key: 'US:CA', + label: 'United States (US) — California', + }, + { + key: 'US:CO', + label: 'United States (US) — Colorado', + }, + { + key: 'US:CT', + label: 'United States (US) — Connecticut', + }, + { + key: 'US:DE', + label: 'United States (US) — Delaware', + }, + { + key: 'US:DC', + label: 'United States (US) — District Of Columbia', + }, + { + key: 'US:FL', + label: 'United States (US) — Florida', + }, + { + key: 'US:GA', + label: 'United States (US) — Georgia', + }, + { + key: 'US:HI', + label: 'United States (US) — Hawaii', + }, + { + key: 'US:ID', + label: 'United States (US) — Idaho', + }, + { + key: 'US:IL', + label: 'United States (US) — Illinois', + }, + { + key: 'US:IN', + label: 'United States (US) — Indiana', + }, + { + key: 'US:IA', + label: 'United States (US) — Iowa', + }, + { + key: 'US:KS', + label: 'United States (US) — Kansas', + }, + { + key: 'US:KY', + label: 'United States (US) — Kentucky', + }, + { + key: 'US:LA', + label: 'United States (US) — Louisiana', + }, + { + key: 'US:ME', + label: 'United States (US) — Maine', + }, + { + key: 'US:MD', + label: 'United States (US) — Maryland', + }, + { + key: 'US:MA', + label: 'United States (US) — Massachusetts', + }, + { + key: 'US:MI', + label: 'United States (US) — Michigan', + }, + { + key: 'US:MN', + label: 'United States (US) — Minnesota', + }, + { + key: 'US:MS', + label: 'United States (US) — Mississippi', + }, + { + key: 'US:MO', + label: 'United States (US) — Missouri', + }, + { + key: 'US:MT', + label: 'United States (US) — Montana', + }, + { + key: 'US:NE', + label: 'United States (US) — Nebraska', + }, + { + key: 'US:NV', + label: 'United States (US) — Nevada', + }, + { + key: 'US:NH', + label: 'United States (US) — New Hampshire', + }, + { + key: 'US:NJ', + label: 'United States (US) — New Jersey', + }, + { + key: 'US:NM', + label: 'United States (US) — New Mexico', + }, + { + key: 'US:NY', + label: 'United States (US) — New York', + }, + { + key: 'US:NC', + label: 'United States (US) — North Carolina', + }, + { + key: 'US:ND', + label: 'United States (US) — North Dakota', + }, + { + key: 'US:OH', + label: 'United States (US) — Ohio', + }, + { + key: 'US:OK', + label: 'United States (US) — Oklahoma', + }, + { + key: 'US:OR', + label: 'United States (US) — Oregon', + }, + { + key: 'US:PA', + label: 'United States (US) — Pennsylvania', + }, + { + key: 'US:RI', + label: 'United States (US) — Rhode Island', + }, + { + key: 'US:SC', + label: 'United States (US) — South Carolina', + }, + { + key: 'US:SD', + label: 'United States (US) — South Dakota', + }, + { + key: 'US:TN', + label: 'United States (US) — Tennessee', + }, + { + key: 'US:TX', + label: 'United States (US) — Texas', + }, + { + key: 'US:UT', + label: 'United States (US) — Utah', + }, + { + key: 'US:VT', + label: 'United States (US) — Vermont', + }, + { + key: 'US:VA', + label: 'United States (US) — Virginia', + }, + { + key: 'US:WA', + label: 'United States (US) — Washington', + }, + { + key: 'US:WV', + label: 'United States (US) — West Virginia', + }, + { + key: 'US:WI', + label: 'United States (US) — Wisconsin', + }, + { + key: 'US:WY', + label: 'United States (US) — Wyoming', + }, + { + key: 'US:AA', + label: 'United States (US) — Armed Forces (AA)', + }, + { + key: 'US:AE', + label: 'United States (US) — Armed Forces (AE)', + }, + { + key: 'US:AP', + label: 'United States (US) — Armed Forces (AP)', + }, + { + key: 'UM:81', + label: 'United States (US) Minor Outlying Islands — Baker Island', + }, + { + key: 'UM:84', + label: 'United States (US) Minor Outlying Islands — Howland Island', + }, + { + key: 'UM:86', + label: 'United States (US) Minor Outlying Islands — Jarvis Island', + }, + { + key: 'UM:67', + label: 'United States (US) Minor Outlying Islands — Johnston Atoll', + }, + { + key: 'UM:89', + label: 'United States (US) Minor Outlying Islands — Kingman Reef', + }, + { + key: 'UM:71', + label: 'United States (US) Minor Outlying Islands — Midway Atoll', + }, + { + key: 'UM:76', + label: 'United States (US) Minor Outlying Islands — Navassa Island', + }, + { + key: 'UM:95', + label: 'United States (US) Minor Outlying Islands — Palmyra Atoll', + }, + { + key: 'UM:79', + label: 'United States (US) Minor Outlying Islands — Wake Island', + }, + { + key: 'UY:UY-AR', + label: 'Uruguay — Artigas', + }, + { + key: 'UY:UY-CA', + label: 'Uruguay — Canelones', + }, + { + key: 'UY:UY-CL', + label: 'Uruguay — Cerro Largo', + }, + { + key: 'UY:UY-CO', + label: 'Uruguay — Colonia', + }, + { + key: 'UY:UY-DU', + label: 'Uruguay — Durazno', + }, + { + key: 'UY:UY-FS', + label: 'Uruguay — Flores', + }, + { + key: 'UY:UY-FD', + label: 'Uruguay — Florida', + }, + { + key: 'UY:UY-LA', + label: 'Uruguay — Lavalleja', + }, + { + key: 'UY:UY-MA', + label: 'Uruguay — Maldonado', + }, + { + key: 'UY:UY-MO', + label: 'Uruguay — Montevideo', + }, + { + key: 'UY:UY-PA', + label: 'Uruguay — Paysandú', + }, + { + key: 'UY:UY-RN', + label: 'Uruguay — Río Negro', + }, + { + key: 'UY:UY-RV', + label: 'Uruguay — Rivera', + }, + { + key: 'UY:UY-RO', + label: 'Uruguay — Rocha', + }, + { + key: 'UY:UY-SA', + label: 'Uruguay — Salto', + }, + { + key: 'UY:UY-SJ', + label: 'Uruguay — San José', + }, + { + key: 'UY:UY-SO', + label: 'Uruguay — Soriano', + }, + { + key: 'UY:UY-TA', + label: 'Uruguay — Tacuarembó', + }, + { + key: 'UY:UY-TT', + label: 'Uruguay — Treinta y Tres', + }, + { + key: 'UZ', + label: 'Uzbekistan', + }, + { + key: 'VU', + label: 'Vanuatu', + }, + { + key: 'VA', + label: 'Vatican', + }, + { + key: 'VE:VE-A', + label: 'Venezuela — Capital', + }, + { + key: 'VE:VE-B', + label: 'Venezuela — Anzoátegui', + }, + { + key: 'VE:VE-C', + label: 'Venezuela — Apure', + }, + { + key: 'VE:VE-D', + label: 'Venezuela — Aragua', + }, + { + key: 'VE:VE-E', + label: 'Venezuela — Barinas', + }, + { + key: 'VE:VE-F', + label: 'Venezuela — Bolívar', + }, + { + key: 'VE:VE-G', + label: 'Venezuela — Carabobo', + }, + { + key: 'VE:VE-H', + label: 'Venezuela — Cojedes', + }, + { + key: 'VE:VE-I', + label: 'Venezuela — Falcón', + }, + { + key: 'VE:VE-J', + label: 'Venezuela — Guárico', + }, + { + key: 'VE:VE-K', + label: 'Venezuela — Lara', + }, + { + key: 'VE:VE-L', + label: 'Venezuela — Mérida', + }, + { + key: 'VE:VE-M', + label: 'Venezuela — Miranda', + }, + { + key: 'VE:VE-N', + label: 'Venezuela — Monagas', + }, + { + key: 'VE:VE-O', + label: 'Venezuela — Nueva Esparta', + }, + { + key: 'VE:VE-P', + label: 'Venezuela — Portuguesa', + }, + { + key: 'VE:VE-R', + label: 'Venezuela — Sucre', + }, + { + key: 'VE:VE-S', + label: 'Venezuela — Táchira', + }, + { + key: 'VE:VE-T', + label: 'Venezuela — Trujillo', + }, + { + key: 'VE:VE-U', + label: 'Venezuela — Yaracuy', + }, + { + key: 'VE:VE-V', + label: 'Venezuela — Zulia', + }, + { + key: 'VE:VE-W', + label: 'Venezuela — Federal Dependencies', + }, + { + key: 'VE:VE-X', + label: 'Venezuela — La Guaira (Vargas)', + }, + { + key: 'VE:VE-Y', + label: 'Venezuela — Delta Amacuro', + }, + { + key: 'VE:VE-Z', + label: 'Venezuela — Amazonas', + }, + { + key: 'VN', + label: 'Vietnam', + }, + { + key: 'VG', + label: 'Virgin Islands (British)', + }, + { + key: 'VI', + label: 'Virgin Islands (US)', + }, + { + key: 'WF', + label: 'Wallis and Futuna', + }, + { + key: 'EH', + label: 'Western Sahara', + }, + { + key: 'YE', + label: 'Yemen', + }, + { + key: 'ZM:ZM-01', + label: 'Zambia — Western', + }, + { + key: 'ZM:ZM-02', + label: 'Zambia — Central', + }, + { + key: 'ZM:ZM-03', + label: 'Zambia — Eastern', + }, + { + key: 'ZM:ZM-04', + label: 'Zambia — Luapula', + }, + { + key: 'ZM:ZM-05', + label: 'Zambia — Northern', + }, + { + key: 'ZM:ZM-06', + label: 'Zambia — North-Western', + }, + { + key: 'ZM:ZM-07', + label: 'Zambia — Southern', + }, + { + key: 'ZM:ZM-08', + label: 'Zambia — Copperbelt', + }, + { + key: 'ZM:ZM-09', + label: 'Zambia — Lusaka', + }, + { + key: 'ZM:ZM-10', + label: 'Zambia — Muchinga', + }, + { + key: 'ZW', + label: 'Zimbabwe', + }, +]; diff --git a/packages/js/onboarding/src/utils/countries/tests/utils/index.test.ts b/packages/js/onboarding/src/utils/countries/tests/utils/index.test.ts new file mode 100644 index 00000000000..cb3546d99ec --- /dev/null +++ b/packages/js/onboarding/src/utils/countries/tests/utils/index.test.ts @@ -0,0 +1,96 @@ +/* eslint-disable camelcase */ +/* eslint-disable no-undef */ + +/** + * Internal dependencies + */ +import { findCountryOption } from '../../'; +import { countryStateOptions } from './country-options'; +import { locations } from './locations'; + +describe( 'findCountryOption', () => { + it( 'should return null on undefined location', () => { + const location = undefined; + expect( findCountryOption( countryStateOptions, location ) ).toEqual( + null + ); + } ); + + it( 'should return null when not found', () => { + const location = { country_short: 'US' }; + expect( findCountryOption( countryStateOptions, location ) ).toBeNull(); + } ); + + it.each( [ + { + location: { country_short: 'TW' }, + expected: { + key: 'TW', + label: 'Taiwan', + }, + }, + { + location: { country_short: 'US', region: 'California' }, + expected: { + key: 'US:CA', + label: 'United States (US) — California', + }, + }, + { + location: { country_short: 'ES', region: 'Madrid, Comunidad de' }, + expected: { + key: 'ES:M', + label: 'Spain — Madrid', + }, + }, + { + location: { + country_short: 'AR', + region: 'Ciudad Autonoma de Buenos Aires', + }, + expected: { + key: 'AR:C', + label: 'Argentina — Ciudad Autónoma de Buenos Aires', + }, + }, + { + location: { + country_short: 'IT', + region: 'Lazio', + city: 'Rome', + }, + expected: { + key: 'IT:RM', + label: 'Italy — Roma', + }, + }, + { + location: { + country_short: 'PH', + region: 'National Capital Region', + city: 'Makati', + }, + expected: { + key: 'PH:00', + label: 'Philippines — Metro Manila', + }, + }, + ] )( + 'should return the country option for location $expected', + ( { location, expected } ) => { + expect( + findCountryOption( countryStateOptions, location, 0.4 ) + ).toEqual( expected ); + } + ); + + it( 'should return a country option for > 98% locations', () => { + let matchCount = 0; + locations.forEach( ( location ) => { + if ( findCountryOption( countryStateOptions, location, 0.4 ) ) { + matchCount++; + } + } ); + expect( matchCount / locations.length ).toBeGreaterThan( 0.98 ); + } ); +} ); diff --git a/packages/js/onboarding/src/utils/countries/tests/utils/location-mapping.test.ts b/packages/js/onboarding/src/utils/countries/tests/utils/location-mapping.test.ts new file mode 100644 index 00000000000..bca1ea20330 --- /dev/null +++ b/packages/js/onboarding/src/utils/countries/tests/utils/location-mapping.test.ts @@ -0,0 +1,41 @@ +/* eslint-disable camelcase */ + +/** + * Internal dependencies + */ +import { getMappingRegion } from '../../location-mapping'; + +describe( 'getMappingRegion', () => { + it( 'should return null for an empty location', () => { + expect( getMappingRegion( {} ) ).toBeNull(); + } ); + + it( 'should return null for a location that is not in the mapping', () => { + expect( + getMappingRegion( { country_short: 'US', region: 'California' } ) + ).toBeNull(); + } ); + + it( 'should return null for a location with no region', () => { + expect( getMappingRegion( { country_short: 'PH' } ) ).toBeNull(); + } ); + + it( 'should return the region for a location that is in the mapping with a region', () => { + expect( + getMappingRegion( { + country_short: 'PH', + region: 'National Capital Region', + } ) + ).toBe( 'Metro Manila' ); + } ); + + it( 'should return the region for a location that is in the mapping with a city', () => { + expect( + getMappingRegion( { + country_short: 'IT', + region: 'Lazio', + city: 'Rome', + } ) + ).toBe( 'Roma' ); + } ); +} ); diff --git a/packages/js/onboarding/src/utils/countries/tests/utils/locations.ts b/packages/js/onboarding/src/utils/countries/tests/utils/locations.ts new file mode 100644 index 00000000000..5a40af0bd63 --- /dev/null +++ b/packages/js/onboarding/src/utils/countries/tests/utils/locations.ts @@ -0,0 +1,17862 @@ +export const locations = [ + { + country_short: 'US', + region: 'California', + city: 'Los Angeles', + }, + { + country_short: 'CN', + region: 'Fujian', + city: 'Fuzhou', + }, + { + country_short: 'AU', + region: 'Tasmania', + city: 'Glebe', + }, + { + country_short: 'AU', + region: 'Victoria', + city: 'Melbourne', + }, + { + country_short: 'CN', + region: 'Guangdong', + city: 'Guangzhou', + }, + { + country_short: 'JP', + region: 'Tokyo', + city: 'Tokyo', + }, + { + country_short: 'JP', + region: 'Hiroshima', + city: 'Hiroshima', + }, + { + country_short: 'JP', + region: 'Miyagi', + city: 'Sendai', + }, + { + country_short: 'JP', + region: 'Shimane', + city: 'Izumo', + }, + { + country_short: 'JP', + region: 'Yamaguchi', + city: 'Yamaguchi', + }, + { + country_short: 'JP', + region: 'Tottori', + city: 'Kurayoshi', + }, + { + country_short: 'JP', + region: 'Okayama', + city: 'Okayama', + }, + { + country_short: 'TH', + region: 'Chiang Rai', + city: 'Pa Daet', + }, + { + country_short: 'TH', + region: 'Krung Thep Maha Nakhon', + city: 'Bangkok', + }, + { + country_short: 'TH', + region: 'Phetchaburi', + city: 'Phetchaburi', + }, + { + country_short: 'TH', + region: 'Songkhla', + city: 'Songkhla', + }, + { + country_short: 'TH', + region: 'Suphan Buri', + city: 'Don Chedi', + }, + { + country_short: 'TH', + region: 'Lop Buri', + city: 'Phatthana Nikhom', + }, + { + country_short: 'TH', + region: 'Ratchaburi', + city: 'Ratchaburi', + }, + { + country_short: 'TH', + region: 'Trang', + city: 'Ratsada', + }, + { + country_short: 'TH', + region: 'Nakhon Sawan', + city: 'Takhli', + }, + { + country_short: 'TH', + region: 'Chanthaburi', + city: 'Laem Sing', + }, + { + country_short: 'TH', + region: 'Chon Buri', + city: 'Chon Buri', + }, + { + country_short: 'TH', + region: 'Surat Thani', + city: 'Ko Samui', + }, + { + country_short: 'TH', + region: 'Phangnga', + city: 'Kapong', + }, + { + country_short: 'TH', + region: 'Chumphon', + city: 'Chumphon', + }, + { + country_short: 'TH', + region: 'Nakhon Si Thammarat', + city: 'Thung Song', + }, + { + country_short: 'TH', + region: 'Krabi', + city: 'Ao Luek', + }, + { + country_short: 'TH', + region: 'Ranong', + city: 'Kra Buri', + }, + { + country_short: 'TH', + region: 'Phatthalung', + city: 'Srinagarindra', + }, + { + country_short: 'TH', + region: 'Chiang Mai', + city: 'San Pa Tong', + }, + { + country_short: 'TH', + region: 'Chachoengsao', + city: 'Bang Pakong', + }, + { + country_short: 'TH', + region: 'Rayong', + city: 'Klaeng', + }, + { + country_short: 'TH', + region: 'Sa Kaeo', + city: 'Aranyaprathet', + }, + { + country_short: 'TH', + region: 'Phuket', + city: 'Phuket', + }, + { + country_short: 'TH', + region: 'Samut Prakan', + city: 'Phra Pradaeng', + }, + { + country_short: 'TH', + region: 'Kamphaeng Phet', + city: 'Pang Sila Thong', + }, + { + country_short: 'TH', + region: 'Phetchabun', + city: 'Lom Kao', + }, + { + country_short: 'TH', + region: 'Phra Nakhon Si Ayutthaya', + city: 'Nakhon Luang', + }, + { + country_short: 'TH', + region: 'Nakhon Pathom', + city: 'Sam Phran', + }, + { + country_short: 'TH', + region: 'Narathiwat', + city: 'Tak Bai', + }, + { + country_short: 'TH', + region: 'Nan', + city: 'Phu Phiang', + }, + { + country_short: 'TH', + region: 'Nakhon Phanom', + city: 'Renu Nakhon', + }, + { + country_short: 'TH', + region: 'Udon Thani', + city: 'Udon Thani', + }, + { + country_short: 'TH', + region: 'Kalasin', + city: 'Kuchinarai', + }, + { + country_short: 'TH', + region: 'Loei', + city: 'Wang Saphung', + }, + { + country_short: 'TH', + region: 'Bueng Kan', + city: 'Ban Mai Phatthana', + }, + { + country_short: 'TH', + region: 'Yala', + city: 'Yala', + }, + { + country_short: 'TH', + region: 'Pattani', + city: 'Mae Lan', + }, + { + country_short: 'CN', + region: 'Shanghai', + city: 'Shanghai', + }, + { + country_short: 'TH', + region: 'Sakon Nakhon', + city: 'Wanon Niwat', + }, + { + country_short: 'TH', + region: 'Roi Et', + city: 'Roi Et', + }, + { + country_short: 'TH', + region: 'Mukdahan', + city: 'Khamcha-i', + }, + { + country_short: 'TH', + region: 'Khon Kaen', + city: 'Khon Kaen', + }, + { + country_short: 'TH', + region: 'Phrae', + city: 'Sung Men', + }, + { + country_short: 'TH', + region: 'Lamphun', + city: 'Mae Tha', + }, + { + country_short: 'TH', + region: 'Pathum Thani', + city: 'Ban Lam Luk Ka', + }, + { + country_short: 'TH', + region: 'Nonthaburi', + city: 'Mueang Nonthaburi', + }, + { + country_short: 'TH', + region: 'Yasothon', + city: 'Yasothon', + }, + { + country_short: 'TH', + region: 'Nakhon Ratchasima', + city: 'Nakhon Ratchasima', + }, + { + country_short: 'TH', + region: 'Trat', + city: 'Laem Ngop', + }, + { + country_short: 'TH', + region: 'Nong Bua Lam Phu', + city: 'Si Bun Rueang', + }, + { + country_short: 'TH', + region: 'Amnat Charoen', + city: 'Hua Taphan', + }, + { + country_short: 'TH', + region: 'Maha Sarakham', + city: 'Maha Sarakham', + }, + { + country_short: 'TH', + region: 'Lampang', + city: 'Ngao', + }, + { + country_short: 'TH', + region: 'Prachuap Khiri Khan', + city: 'Bang Saphan Noi', + }, + { + country_short: 'TH', + region: 'Si Sa Ket', + city: 'Huai Thap Than', + }, + { + country_short: 'TH', + region: 'Prachin Buri', + city: 'Prachin Buri', + }, + { + country_short: 'TH', + region: 'Phitsanulok', + city: 'Phitsanulok', + }, + { + country_short: 'TH', + region: 'Buri Ram', + city: 'Prakhon Chai', + }, + { + country_short: 'CN', + region: 'Beijing', + city: 'Beijing', + }, + { + country_short: 'AU', + region: 'Queensland', + city: 'Brisbane', + }, + { + country_short: 'TH', + region: 'Uttaradit', + city: 'Uttaradit', + }, + { + country_short: 'TH', + region: 'Satun', + city: 'Satun', + }, + { + country_short: 'TH', + region: 'Uthai Thani', + city: 'Uthai Thani', + }, + { + country_short: 'TH', + region: 'Phayao', + city: 'Phayao', + }, + { + country_short: 'TH', + region: 'Nong Khai', + city: 'Nong Khai', + }, + { + country_short: 'TH', + region: 'Tak', + city: 'Umphang', + }, + { + country_short: 'TH', + region: 'Phichit', + city: 'Taphan Hin', + }, + { + country_short: 'TH', + region: 'Samut Songkhram', + city: 'Samut Songkhram', + }, + { + country_short: 'TH', + region: 'Samut Sakhon', + city: 'Krathum Baen', + }, + { + country_short: 'TH', + region: 'Ubon Ratchathani', + city: 'Na Chaluai', + }, + { + country_short: 'JP', + region: 'Gifu', + city: 'Gifu', + }, + { + country_short: 'IN', + region: 'Karnataka', + city: 'Bengaluru', + }, + { + country_short: 'IN', + region: 'Tamil Nadu', + city: 'Chennai', + }, + { + country_short: 'IN', + region: 'Maharashtra', + city: 'Mumbai', + }, + { + country_short: 'IN', + region: 'Chandigarh', + city: 'Chandigarh', + }, + { + country_short: 'IN', + region: 'West Bengal', + city: 'Kolkata', + }, + { + country_short: 'IN', + region: 'Haryana', + city: 'Gurgaon', + }, + { + country_short: 'IN', + region: 'Madhya Pradesh', + city: 'Bhopal', + }, + { + country_short: 'IN', + region: 'Delhi', + city: 'Delhi', + }, + { + country_short: 'IN', + region: 'Jharkhand', + city: 'Jamshedpur', + }, + { + country_short: 'IN', + region: 'Bihar', + city: 'Patna', + }, + { + country_short: 'IN', + region: 'Odisha', + city: 'Bhubaneshwar', + }, + { + country_short: 'IN', + region: 'Mizoram', + city: 'Aizawl', + }, + { + country_short: 'IN', + region: 'Gujarat', + city: 'Kathor', + }, + { + country_short: 'MY', + region: 'Selangor', + city: 'Shah Alam', + }, + { + country_short: 'MY', + region: 'Wilayah Persekutuan Kuala Lumpur', + city: 'Kuala Lumpur', + }, + { + country_short: 'MY', + region: 'Negeri Sembilan', + city: 'Bahau', + }, + { + country_short: 'MY', + region: 'Sarawak', + city: 'Kuching', + }, + { + country_short: 'MY', + region: 'Sabah', + city: 'Bandar Labuan', + }, + { + country_short: 'MY', + region: 'Johor', + city: 'Johor Bahru', + }, + { + country_short: 'MY', + region: 'Kedah', + city: 'Pulau Langkawi', + }, + { + country_short: 'MY', + region: 'Pulau Pinang', + city: 'George Town', + }, + { + country_short: 'MY', + region: 'Perak', + city: 'Ipoh', + }, + { + country_short: 'MY', + region: 'Terengganu', + city: 'Kuala Terengganu', + }, + { + country_short: 'MY', + region: 'Melaka', + city: 'Melaka', + }, + { + country_short: 'MY', + region: 'Pahang', + city: 'Bentong Town', + }, + { + country_short: 'MY', + region: 'Kelantan', + city: 'Peringat', + }, + { + country_short: 'TH', + region: 'Mae Hong Son', + city: 'Mae Hong Son', + }, + { + country_short: 'TH', + region: 'Kanchanaburi', + city: 'Nong Prue', + }, + { + country_short: 'TH', + region: 'Sing Buri', + city: 'Sing Buri', + }, + { + country_short: 'TH', + region: 'Surin', + city: 'Lamduan', + }, + { + country_short: 'TH', + region: 'Saraburi', + city: 'Wang Muang', + }, + { + country_short: 'KR', + region: 'Seoul-teukbyeolsi', + city: 'Seoul', + }, + { + country_short: 'KR', + region: 'Incheon-gwangyeoksi', + city: 'Namdong', + }, + { + country_short: 'KR', + region: 'Gyeonggi-do', + city: 'Bucheon', + }, + { + country_short: 'KR', + region: 'Chungcheongnam-do', + city: 'Dangjin', + }, + { + country_short: 'KR', + region: 'Gwangju-gwangyeoksi', + city: 'Gwangju', + }, + { + country_short: 'CN', + region: 'Henan', + city: 'Zhengzhou', + }, + { + country_short: 'KR', + region: 'Jeollanam-do', + city: 'Naju', + }, + { + country_short: 'TH', + region: 'Nakhon Nayok', + city: 'Ongkharak', + }, + { + country_short: 'TH', + region: 'Chai Nat', + city: 'Sapphaya', + }, + { + country_short: 'TH', + region: 'Sukhothai', + city: 'Sukhothai', + }, + { + country_short: 'TH', + region: 'Ang Thong', + city: 'Ang Thong', + }, + { + country_short: 'JP', + region: 'Kochi', + city: 'Aki', + }, + { + country_short: 'JP', + region: 'Aichi', + city: 'Nagoya', + }, + { + country_short: 'JP', + region: 'Saitama', + city: 'Saitama', + }, + { + country_short: 'JP', + region: 'Osaka', + city: 'Osaka', + }, + { + country_short: 'JP', + region: 'Ibaraki', + city: 'Tsukuba', + }, + { + country_short: 'JP', + region: 'Kyoto', + city: 'Kyoto', + }, + { + country_short: 'JP', + region: 'Fukuoka', + city: 'Fukuoka', + }, + { + country_short: 'JP', + region: 'Kanagawa', + city: 'Yokohama', + }, + { + country_short: 'JP', + region: 'Hokkaido', + city: 'Sapporo', + }, + { + country_short: 'IN', + region: 'Uttar Pradesh', + city: 'Noida', + }, + { + country_short: 'IN', + region: 'Andhra Pradesh', + city: 'Vepagunta', + }, + { + country_short: 'IN', + region: 'Telangana', + city: 'Hyderabad', + }, + { + country_short: 'IN', + region: 'Rajasthan', + city: 'Jaipur', + }, + { + country_short: 'IN', + region: 'Chhattisgarh', + city: 'Raipur', + }, + { + country_short: 'IN', + region: 'Kerala', + city: 'Changanacheri', + }, + { + country_short: 'CN', + region: 'Nei Mongol', + city: 'Baotou', + }, + { + country_short: 'MY', + region: 'Wilayah Persekutuan Putrajaya', + city: 'Putrajaya', + }, + { + country_short: 'SG', + region: 'Singapore', + city: 'Singapore', + }, + { + country_short: 'HK', + region: 'Hong Kong', + city: 'Hong Kong', + }, + { + country_short: 'TW', + region: 'Taichung', + city: 'Taichung', + }, + { + country_short: 'TW', + region: 'Taipei', + city: 'Taipei', + }, + { + country_short: 'KH', + region: 'Phnom Penh', + city: 'Phnom Penh', + }, + { + country_short: 'JP', + region: 'Shizuoka', + city: 'Hamamatsu', + }, + { + country_short: 'JP', + region: 'Chiba', + city: 'Chiba', + }, + { + country_short: 'JP', + region: 'Yamanashi', + city: 'Kofu', + }, + { + country_short: 'JP', + region: 'Akita', + city: 'Akita', + }, + { + country_short: 'JP', + region: 'Iwate', + city: 'Ichinoseki', + }, + { + country_short: 'JP', + region: 'Nagano', + city: 'Matsumoto', + }, + { + country_short: 'JP', + region: 'Niigata', + city: 'Niigata', + }, + { + country_short: 'JP', + region: 'Fukushima', + city: 'Shirakawa', + }, + { + country_short: 'JP', + region: 'Tochigi', + city: 'Utsunomiya', + }, + { + country_short: 'JP', + region: 'Gunma', + city: 'Maebashi', + }, + { + country_short: 'JP', + region: 'Hyogo', + city: 'Amagasaki', + }, + { + country_short: 'JP', + region: 'Yamagata', + city: 'Sakata', + }, + { + country_short: 'JP', + region: 'Nagasaki', + city: 'Nagasaki', + }, + { + country_short: 'JP', + region: 'Saga', + city: 'Karatsu', + }, + { + country_short: 'JP', + region: 'Aomori', + city: 'Aomori', + }, + { + country_short: 'JP', + region: 'Toyama', + city: 'Namerikawa', + }, + { + country_short: 'JP', + region: 'Okinawa', + city: 'Naha', + }, + { + country_short: 'JP', + region: 'Nara', + city: 'Gose', + }, + { + country_short: 'JP', + region: 'Tokushima', + city: 'Tokushima', + }, + { + country_short: 'JP', + region: 'Miyazaki', + city: 'Miyazaki', + }, + { + country_short: 'JP', + region: 'Kagoshima', + city: 'Amami', + }, + { + country_short: 'JP', + region: 'Mie', + city: 'Tsu', + }, + { + country_short: 'JP', + region: 'Ehime', + city: 'Saijo', + }, + { + country_short: 'JP', + region: 'Fukui', + city: 'Fukui', + }, + { + country_short: 'JP', + region: 'Wakayama', + city: 'Wakayama', + }, + { + country_short: 'BR', + region: 'Ceara', + city: 'Fortaleza', + }, + { + country_short: 'JP', + region: 'Kumamoto', + city: 'Kumamoto', + }, + { + country_short: 'TW', + region: 'New Taipei', + city: 'Banqiao', + }, + { + country_short: 'TW', + region: 'Taoyuan', + city: 'Taoyuan', + }, + { + country_short: 'TW', + region: 'Hsinchu', + city: 'Hsinchu', + }, + { + country_short: 'TW', + region: 'Keelung', + city: 'Keelung', + }, + { + country_short: 'PH', + region: 'Tarlac', + city: 'Salcedo', + }, + { + country_short: 'PH', + region: 'Iloilo', + city: 'Iloilo', + }, + { + country_short: 'PH', + region: 'National Capital Region', + city: 'Makati', + }, + { + country_short: 'PH', + region: 'Sulu', + city: 'Anuling', + }, + { + country_short: 'PH', + region: 'Agusan del Sur', + city: 'Alegria', + }, + { + country_short: 'PH', + region: 'Pampanga', + city: 'Arayat', + }, + { + country_short: 'PH', + region: 'Pangasinan', + city: 'Tagudin', + }, + { + country_short: 'PH', + region: 'Leyte', + city: 'Tanauan', + }, + { + country_short: 'PH', + region: 'Palawan', + city: 'Puerto Princesa', + }, + { + country_short: 'IN', + region: 'Goa', + city: 'Panaji', + }, + { + country_short: 'IN', + region: 'Manipur', + city: 'Imphal', + }, + { + country_short: 'IN', + region: 'Assam', + city: 'Dimapur', + }, + { + country_short: 'IN', + region: 'Tripura', + city: 'Agartala', + }, + { + country_short: 'IN', + region: 'Puducherry', + city: 'Puducherry', + }, + { + country_short: 'AU', + region: 'New South Wales', + city: 'Sydney', + }, + { + country_short: 'AU', + region: 'Australian Capital Territory', + city: 'Canberra', + }, + { + country_short: 'AU', + region: 'Northern Territory', + city: 'Titjikala', + }, + { + country_short: 'TH', + region: 'Chaiyaphum', + city: 'Chatturat', + }, + { + country_short: 'CN', + region: 'Guizhou', + city: 'Anshun', + }, + { + country_short: 'CN', + region: 'Ningxia Huizu', + city: 'Yinchuan', + }, + { + country_short: 'CN', + region: 'Jiangsu', + city: 'Nanjing', + }, + { + country_short: 'VN', + region: 'Ha Noi', + city: 'Hanoi', + }, + { + country_short: 'VN', + region: 'Ho Chi Minh', + city: 'Ho Chi Minh City', + }, + { + country_short: 'VN', + region: 'Hai Duong', + city: 'Hai Duong', + }, + { + country_short: 'VN', + region: 'Dong Nai', + city: 'Bien Hoa', + }, + { + country_short: 'VN', + region: 'Da Nang', + city: 'Da Nang', + }, + { + country_short: 'VN', + region: 'Phu Yen', + city: 'Tuy Hoa', + }, + { + country_short: 'VN', + region: 'Ca Mau', + city: 'Ca Mau', + }, + { + country_short: 'VN', + region: 'Binh Duong', + city: 'Thu Dau Mot', + }, + { + country_short: 'VN', + region: 'Khanh Hoa', + city: 'Nha Trang', + }, + { + country_short: 'VN', + region: 'Dak Lak', + city: 'Buon Ma Thuot', + }, + { + country_short: 'VN', + region: 'Quang Ninh', + city: 'Ha Long', + }, + { + country_short: 'VN', + region: 'Bac Giang', + city: 'Thang', + }, + { + country_short: 'VN', + region: 'Tay Ninh', + city: 'Phu Khuong', + }, + { + country_short: 'VN', + region: 'Ba Ria - Vung Tau', + city: 'Phuoc Lap', + }, + { + country_short: 'VN', + region: 'Thua Thien-Hue', + city: 'Hue', + }, + { + country_short: 'VN', + region: 'Quang Ngai', + city: 'Quang Ngai', + }, + { + country_short: 'VN', + region: 'Binh Thuan', + city: 'Phan Thiet', + }, + { + country_short: 'VN', + region: 'An Giang', + city: 'Long Xuyen', + }, + { + country_short: 'VN', + region: 'Long An', + city: 'Tan An', + }, + { + country_short: 'VN', + region: 'Can Tho', + city: 'Can Tho', + }, + { + country_short: 'VN', + region: 'Ben Tre', + city: 'Ben Tre', + }, + { + country_short: 'VN', + region: 'Bac Ninh', + city: 'Bac Ninh', + }, + { + country_short: 'VN', + region: 'Lam Dong', + city: 'Da Lat', + }, + { + country_short: 'VN', + region: 'Kon Tum', + city: 'Kon Tum', + }, + { + country_short: 'VN', + region: 'Thai Nguyen', + city: 'Thai Nguyen', + }, + { + country_short: 'VN', + region: 'Hung Yen', + city: 'Hung Yen', + }, + { + country_short: 'VN', + region: 'Thanh Hoa', + city: 'Thanh Hoa', + }, + { + country_short: 'VN', + region: 'Yen Bai', + city: 'Yen Bai', + }, + { + country_short: 'VN', + region: 'Ninh Thuan', + city: 'Phan Rang-Thap Cham', + }, + { + country_short: 'VN', + region: 'Gia Lai', + city: 'Pleiku', + }, + { + country_short: 'VN', + region: 'Lang Son', + city: 'Lang Son', + }, + { + country_short: 'VN', + region: 'Kien Giang', + city: 'Rach Gia', + }, + { + country_short: 'VN', + region: 'Binh Dinh', + city: 'Qui Nhon', + }, + { + country_short: 'VN', + region: 'Hai Phong', + city: 'Haiphong', + }, + { + country_short: 'VN', + region: 'Soc Trang', + city: 'Soc Trang', + }, + { + country_short: 'VN', + region: 'Ninh Binh', + city: 'Ninh Binh', + }, + { + country_short: 'VN', + region: 'Thai Binh', + city: 'Thai Binh', + }, + { + country_short: 'VN', + region: 'Dien Bien', + city: 'Dien Bien Phu', + }, + { + country_short: 'VN', + region: 'Quang Binh', + city: 'Dong Hoi', + }, + { + country_short: 'VN', + region: 'Quang Nam', + city: 'Hoi An', + }, + { + country_short: 'VN', + region: 'Nghe An', + city: 'Vinh', + }, + { + country_short: 'VN', + region: 'Tra Vinh', + city: 'Tra Vinh', + }, + { + country_short: 'VN', + region: 'Tien Giang', + city: 'My Tho', + }, + { + country_short: 'VN', + region: 'Vinh Phuc', + city: 'Vinh Yen', + }, + { + country_short: 'VN', + region: 'Vinh Long', + city: 'Vinh Long', + }, + { + country_short: 'VN', + region: 'Phu Tho', + city: 'Viet Tri', + }, + { + country_short: 'VN', + region: 'Tuyen Quang', + city: 'Tuyen Quang', + }, + { + country_short: 'VN', + region: 'Dong Thap', + city: 'Cao Lanh', + }, + { + country_short: 'VN', + region: 'Cao Bang', + city: 'Cao Bang', + }, + { + country_short: 'VN', + region: 'Nam Dinh', + city: 'Nam Dinh', + }, + { + country_short: 'VN', + region: 'Lao Cai', + city: 'Lao Cai', + }, + { + country_short: 'CN', + region: 'Heilongjiang', + city: 'Hegang', + }, + { + country_short: 'JP', + region: 'Oita', + city: 'Oita', + }, + { + country_short: 'JP', + region: 'Kagawa', + city: 'Takamatsu', + }, + { + country_short: 'CN', + region: 'Shanxi', + city: 'Taiyuan', + }, + { + country_short: 'JP', + region: 'Shiga', + city: 'Otsu', + }, + { + country_short: 'CN', + region: 'Shaanxi', + city: "Xi'an", + }, + { + country_short: 'CN', + region: 'Zhejiang', + city: 'Hangzhou', + }, + { + country_short: 'KR', + region: 'Busan-gwangyeoksi', + city: 'Busan', + }, + { + country_short: 'CN', + region: 'Jiangxi', + city: 'Jingdong', + }, + { + country_short: 'AU', + region: 'South Australia', + city: 'Adelaide', + }, + { + country_short: 'AU', + region: 'Western Australia', + city: 'Perth', + }, + { + country_short: 'TW', + region: 'Tainan', + city: 'Tainan', + }, + { + country_short: 'TW', + region: 'Yilan', + city: 'Yilan', + }, + { + country_short: 'TW', + region: 'Hualien', + city: 'Hualien', + }, + { + country_short: 'TW', + region: 'Changhua', + city: 'Changhua', + }, + { + country_short: 'TW', + region: 'Taitung', + city: 'Taitung', + }, + { + country_short: 'TW', + region: 'Yunlin', + city: 'Douliu', + }, + { + country_short: 'TW', + region: 'Chiayi', + city: 'Puzi', + }, + { + country_short: 'TW', + region: 'Nantou', + city: 'Puli', + }, + { + country_short: 'TW', + region: 'Miaoli', + city: 'Miaoli', + }, + { + country_short: 'TW', + region: 'Kaohsiung', + city: 'Kaohsiung', + }, + { + country_short: 'TW', + region: 'Penghu', + city: 'Magong', + }, + { + country_short: 'TW', + region: 'Pingtung', + city: 'Hengchun', + }, + { + country_short: 'TW', + region: 'Kinmen', + city: 'Jincheng', + }, + { + country_short: 'NO', + region: 'Vestfold og Telemark', + city: 'Porsgrunn', + }, + { + country_short: 'KR', + region: 'Gyeongsangnam-do', + city: 'Changwon', + }, + { + country_short: 'KR', + region: 'Gyeongsangbuk-do', + city: 'Andong', + }, + { + country_short: 'BR', + region: 'Bahia', + city: 'Central', + }, + { + country_short: 'AR', + region: 'Ciudad Autonoma de Buenos Aires', + city: 'Buenos Aires', + }, + { + country_short: 'PS', + region: ' State of"', + city: 'Nablus', + }, + { + country_short: 'ES', + region: 'Madrid, Comunidad de', + city: 'Madrid, Comunidad de', + }, + { + country_short: 'FR', + region: 'Ile-de-France', + city: 'Paris', + }, + { + country_short: 'IN', + region: 'Punjab', + city: 'Jandiala Guru', + }, + { + country_short: 'IN', + region: 'Uttarakhand', + city: 'Haridwar', + }, + { + country_short: 'IN', + region: 'Jammu and Kashmir', + city: 'Udhampur', + }, + { + country_short: 'IN', + region: 'Himachal Pradesh', + city: 'Sarka Ghat', + }, + { + country_short: 'IN', + region: 'Meghalaya', + city: 'Shillong', + }, + { + country_short: 'KR', + region: 'Daejeon-gwangyeoksi', + city: 'Daejeon', + }, + { + country_short: 'KR', + region: 'Gangwon-do', + city: 'Yeongwol', + }, + { + country_short: 'KR', + region: 'Ulsan-gwangyeoksi', + city: 'Ulsan', + }, + { + country_short: 'KR', + region: 'Chungcheongbuk-do', + city: 'Cheongju', + }, + { + country_short: 'KR', + region: 'Daegu-gwangyeoksi', + city: 'Daegu', + }, + { + country_short: 'KR', + region: 'Jeollabuk-do', + city: 'Gunsan', + }, + { + country_short: 'KR', + region: 'Jeju-teukbyeoljachido', + city: 'Jeju', + }, + { + country_short: 'FR', + region: 'Pays-de-la-Loire', + city: 'Parcay-les-Pins', + }, + { + country_short: 'FR', + region: 'Centre-Val de Loire', + city: 'Saint-Aignan', + }, + { + country_short: 'FR', + region: 'Normandie', + city: 'Le Teilleul', + }, + { + country_short: 'FR', + region: "Provence-Alpes-Cote-d'Azur", + city: 'Nice', + }, + { + country_short: 'FR', + region: 'Auvergne-Rhone-Alpes', + city: 'Auzat-la-Combelle', + }, + { + country_short: 'FR', + region: 'Occitanie', + city: "Saint-Chely-d'Apcher", + }, + { + country_short: 'FR', + region: 'Hauts-de-France', + city: 'Lille', + }, + { + country_short: 'FR', + region: 'Grand-Est', + city: 'Givet', + }, + { + country_short: 'FR', + region: 'Nouvelle-Aquitaine', + city: 'Port-Sainte-Marie', + }, + { + country_short: 'FR', + region: 'Bourgogne-Franche-Comte', + city: 'Lormes', + }, + { + country_short: 'FR', + region: 'Corse', + city: 'Aleria', + }, + { + country_short: 'FR', + region: 'Bretagne', + city: 'Plumergat', + }, + { + country_short: 'CZ', + region: 'Praha, Hlavni mesto', + city: 'Praha, Hlavni mesto', + }, + { + country_short: 'DE', + region: 'Hessen', + city: 'Frankfurt am Main', + }, + { + country_short: 'AT', + region: 'Wien', + city: 'Vienna', + }, + { + country_short: 'CH', + region: 'Zurich', + city: 'Glattbrugg', + }, + { + country_short: 'GB', + region: 'England', + city: 'London', + }, + { + country_short: 'BR', + region: 'Distrito Federal', + city: 'Brasilia', + }, + { + country_short: 'IT', + region: 'Lazio', + city: 'Rome', + }, + { + country_short: 'NL', + region: 'Noord-Holland', + city: 'Amsterdam', + }, + { + country_short: 'GR', + region: 'Attiki', + city: 'Athens', + }, + { + country_short: 'RU', + region: "Nizhegorodskaya oblast'", + city: 'Nizhniy Novgorod', + }, + { + country_short: 'RU', + region: 'Sankt-Peterburg', + city: 'Saint Petersburg', + }, + { + country_short: 'IT', + region: 'Lombardia', + city: 'Milan', + }, + { + country_short: 'US', + region: 'Virginia', + city: 'Ashburn', + }, + { + country_short: 'DE', + region: 'Hamburg', + city: 'Hamburg', + }, + { + country_short: 'US', + region: 'Florida', + city: 'Miami', + }, + { + country_short: 'DE', + region: 'Bayern', + city: 'Munich', + }, + { + country_short: 'BE', + region: 'Brussels Hoofdstedelijk Gewest', + city: 'Brussels', + }, + { + country_short: 'RU', + region: 'Moskva', + city: 'Moscow', + }, + { + country_short: 'DE', + region: 'Nordrhein-Westfalen', + city: 'Dusseldorf', + }, + { + country_short: 'DK', + region: 'Hovedstaden', + city: 'Ballerup', + }, + { + country_short: 'PT', + region: 'Lisboa', + city: 'Lisbon', + }, + { + country_short: 'SE', + region: 'Stockholms lan', + city: 'Stockholm', + }, + { + country_short: 'IT', + region: 'Sicilia', + city: 'Palermo', + }, + { + country_short: 'GH', + region: 'Greater Accra', + city: 'Accra', + }, + { + country_short: 'IE', + region: 'Dublin', + city: 'Dublin', + }, + { + country_short: 'DE', + region: 'Rheinland-Pfalz', + city: 'Isenburg', + }, + { + country_short: 'BR', + region: 'Rio de Janeiro', + city: 'Rio de Janeiro', + }, + { + country_short: 'US', + region: 'New York', + city: 'New York City', + }, + { + country_short: 'CM', + region: 'Centre', + city: 'Yaounde', + }, + { + country_short: 'ZA', + region: 'Gauteng', + city: 'Johannesburg', + }, + { + country_short: 'TR', + region: 'Istanbul', + city: 'Istanbul', + }, + { + country_short: 'AE', + region: 'Al Fujayrah', + city: 'Al Fujayrah', + }, + { + country_short: 'PL', + region: 'Mazowieckie', + city: 'Warsaw', + }, + { + country_short: 'BR', + region: 'Sao Paulo', + city: 'Sao Paulo', + }, + { + country_short: 'ZA', + region: 'Western Cape', + city: 'Cape Town', + }, + { + country_short: 'BR', + region: 'Parana', + city: 'Curitiba', + }, + { + country_short: 'NO', + region: 'Oslo', + city: 'Oslo', + }, + { + country_short: 'JO', + region: "Al 'Asimah", + city: 'Amman', + }, + { + country_short: 'RO', + region: 'Bucuresti', + city: 'Bucharest', + }, + { + country_short: 'RU', + region: 'Krasnodarskiy kray', + city: 'Krasnodar', + }, + { + country_short: 'RU', + region: "Novosibirskaya oblast'", + city: 'Kochenevo', + }, + { + country_short: 'RU', + region: "Sverdlovskaya oblast'", + city: 'Yekaterinburg', + }, + { + country_short: 'KE', + region: 'Mombasa', + city: 'Mombasa', + }, + { + country_short: 'TR', + region: 'Ankara', + city: 'Ankara', + }, + { + country_short: 'UG', + region: 'Kampala', + city: 'Kampala', + }, + { + country_short: 'AM', + region: 'Erevan', + city: 'Yerevan', + }, + { + country_short: 'TZ', + region: ' United Republic of"', + city: 'Dar es Salaam', + }, + { + country_short: 'US', + region: 'Illinois', + city: 'Chicago', + }, + { + country_short: 'BI', + region: 'Bujumbura Mairie', + city: 'Bujumbura', + }, + { + country_short: 'IE', + region: 'Cork', + city: 'Cork', + }, + { + country_short: 'US', + region: 'Texas', + city: 'Dallas', + }, + { + country_short: 'UY', + region: 'Montevideo', + city: 'Montevideo', + }, + { + country_short: 'CL', + region: 'Maule', + city: 'Constitucion', + }, + { + country_short: 'CL', + region: 'Region Metropolitana de Santiago', + city: 'Santiago', + }, + { + country_short: 'LU', + region: 'Esch-sur-Alzette', + city: 'Bettembourg', + }, + { + country_short: 'US', + region: 'Washington', + city: 'Seattle', + }, + { + country_short: 'DE', + region: 'Berlin', + city: 'Berlin', + }, + { + country_short: 'FI', + region: 'Uusimaa', + city: 'Espoo', + }, + { + country_short: 'BG', + region: 'Sofia (stolitsa)', + city: 'Sofia', + }, + { + country_short: 'BR', + region: 'Piaui', + city: 'Parnaiba', + }, + { + country_short: 'DE', + region: 'Baden-Wurttemberg', + city: 'Bempflingen', + }, + { + country_short: 'DK', + region: 'Midtjylland', + city: 'Arhus', + }, + { + country_short: 'UA', + region: 'Kyiv', + city: 'Kiev', + }, + { + country_short: 'EG', + region: 'Al Iskandariyah', + city: 'Alexandria', + }, + { + country_short: 'SE', + region: 'Uppsala lan', + city: 'Uppsala', + }, + { + country_short: 'CA', + region: 'British Columbia', + city: 'Vancouver', + }, + { + country_short: 'BR', + region: 'Rio Grande do Sul', + city: 'Porto Alegre', + }, + { + country_short: 'IL', + region: 'HaMerkaz', + city: 'Kafr Qasim', + }, + { + country_short: 'RU', + region: 'Bashkortostan, Respublika', + city: 'Bashkortostan, Respublika', + }, + { + country_short: 'QA', + region: 'Ad Dawhah', + city: 'Doha', + }, + { + country_short: 'GB', + region: 'Wales', + city: 'Newport', + }, + { + country_short: 'GB', + region: 'Scotland', + city: 'Hamilton', + }, + { + country_short: 'GB', + region: 'Northern Ireland', + city: 'Omagh', + }, + { + country_short: 'IT', + region: 'Piemonte', + city: 'Ivrea', + }, + { + country_short: 'IT', + region: 'Veneto', + city: 'Venice', + }, + { + country_short: 'IT', + region: 'Liguria', + city: 'Genova', + }, + { + country_short: 'IT', + region: 'Campania', + city: 'Naples', + }, + { + country_short: 'IT', + region: 'Emilia-Romagna', + city: 'Modena', + }, + { + country_short: 'IT', + region: 'Puglia', + city: 'Canosa di Puglia', + }, + { + country_short: 'IT', + region: 'Toscana', + city: 'Florence', + }, + { + country_short: 'IT', + region: "Valle d'Aosta", + city: 'Courmayeur', + }, + { + country_short: 'IT', + region: 'Friuli-Venezia Giulia', + city: 'Pordenone', + }, + { + country_short: 'IT', + region: 'Umbria', + city: 'Perugia', + }, + { + country_short: 'IT', + region: 'Trentino-Alto Adige', + city: 'Borgo', + }, + { + country_short: 'IT', + region: 'Marche', + city: 'Tolentino', + }, + { + country_short: 'IT', + region: 'Sardegna', + city: 'Sassari', + }, + { + country_short: 'IT', + region: 'Abruzzo', + city: 'San Salvo', + }, + { + country_short: 'IT', + region: 'Calabria', + city: 'Amantea', + }, + { + country_short: 'IT', + region: 'Molise', + city: 'Termoli', + }, + { + country_short: 'IT', + region: 'Basilicata', + city: 'Matera', + }, + { + country_short: 'AE', + region: 'Dubayy', + city: 'Dubai', + }, + { + country_short: 'AE', + region: 'Abu Zaby', + city: 'Abu Dhabi', + }, + { + country_short: 'AE', + region: 'Ash Shariqah', + city: 'Sharjah', + }, + { + country_short: 'AE', + region: "Ra's al Khaymah", + city: 'Ras Al Khaimah', + }, + { + country_short: 'AE', + region: "'Ajman", + city: "'Ajman", + }, + { + country_short: 'AE', + region: 'Umm al Qaywayn', + city: 'Umm Al Quwain', + }, + { + country_short: 'IL', + region: 'Tel Aviv', + city: 'Tel Aviv', + }, + { + country_short: 'IL', + region: 'HaTsafon', + city: 'Sulam', + }, + { + country_short: 'IL', + region: 'HaDarom', + city: 'Dimona', + }, + { + country_short: 'IL', + region: 'Hefa', + city: 'Haifa', + }, + { + country_short: 'IL', + region: 'Yerushalayim', + city: 'Jerusalem', + }, + { + country_short: 'MD', + region: 'Chisinau', + city: 'Chisinau', + }, + { + country_short: 'TR', + region: 'Izmir', + city: 'Izmir', + }, + { + country_short: 'BE', + region: 'Antwerpen', + city: 'Dessel', + }, + { + country_short: 'SC', + region: 'English River', + city: 'Victoria', + }, + { + country_short: 'US', + region: 'West Virginia', + city: 'Costa', + }, + { + country_short: 'BG', + region: 'Blagoevgrad', + city: 'Petrich', + }, + { + country_short: 'BG', + region: 'Shumen', + city: 'Shumen', + }, + { + country_short: 'SE', + region: 'Varmlands lan', + city: 'Karlstad', + }, + { + country_short: 'IQ', + region: 'Baghdad', + city: 'Baghdad', + }, + { + country_short: 'US', + region: 'District of Columbia', + city: 'Washington', + }, + { + country_short: 'NL', + region: 'Zuid-Holland', + city: 'Brielle', + }, + { + country_short: 'TR', + region: 'Bursa', + city: 'Bursa', + }, + { + country_short: 'ES', + region: 'Valenciana, Comunidad', + city: 'Valenciana, Comunidad', + }, + { + country_short: 'US', + region: 'Wyoming', + city: 'Cheyenne', + }, + { + country_short: 'ES', + region: 'Extremadura', + city: 'Merida', + }, + { + country_short: 'LU', + region: 'Luxembourg', + city: 'Luxembourg', + }, + { + country_short: 'UA', + region: 'Kharkivska oblast', + city: 'Kharkiv', + }, + { + country_short: 'VA', + region: 'Vatican City', + city: 'Vatican City', + }, + { + country_short: 'US', + region: 'Ohio', + city: 'Poland', + }, + { + country_short: 'US', + region: 'New Jersey', + city: 'Secaucus', + }, + { + country_short: 'CH', + region: 'Bern', + city: 'Bern', + }, + { + country_short: 'UA', + region: 'Kyivska oblast', + city: 'Ukrainka', + }, + { + country_short: 'US', + region: 'Kentucky', + city: 'London', + }, + { + country_short: 'LV', + region: 'Riga', + city: 'Riga', + }, + { + country_short: 'US', + region: 'Utah', + city: 'Salt Lake City', + }, + { + country_short: 'AT', + region: 'Niederosterreich', + city: 'Gostling an der Ybbs', + }, + { + country_short: 'AT', + region: 'Oberosterreich', + city: 'Linz', + }, + { + country_short: 'UA', + region: 'Ternopilska oblast', + city: 'Monastyryska', + }, + { + country_short: 'CH', + region: 'Zug', + city: 'Baar', + }, + { + country_short: 'RU', + region: "Kirovskaya oblast'", + city: "Kazan'", + }, + { + country_short: 'NL', + region: 'Flevoland', + city: 'Dronten', + }, + { + country_short: 'NL', + region: 'Gelderland', + city: 'Horst', + }, + { + country_short: 'US', + region: 'Maryland', + city: 'Baltimore', + }, + { + country_short: 'IS', + region: 'Hofudborgarsvaedi', + city: 'Reykjavik', + }, + { + country_short: 'US', + region: 'Delaware', + city: 'Hockessin', + }, + { + country_short: 'ES', + region: 'Catalunya', + city: 'Palamos', + }, + { + country_short: 'RU', + region: "Moskovskaya oblast'", + city: 'Zhukovskiy', + }, + { + country_short: 'BE', + region: 'Limburg', + city: 'Diepenbeek', + }, + { + country_short: 'RU', + region: "Kaliningradskaya oblast'", + city: 'Kaliningrad', + }, + { + country_short: 'NL', + region: 'Noord-Brabant', + city: 'Valkenswaard', + }, + { + country_short: 'NL', + region: 'Zeeland', + city: 'Vlissingen', + }, + { + country_short: 'SK', + region: 'Bratislavsky kraj', + city: 'Bratislava', + }, + { + country_short: 'RU', + region: "Voronezhskaya oblast'", + city: 'Voronezh', + }, + { + country_short: 'NL', + region: 'Drenthe', + city: 'Meppel', + }, + { + country_short: 'US', + region: 'North Carolina', + city: 'Asheville', + }, + { + country_short: 'KZ', + region: 'Shyghys Qazaqstan oblysy', + city: 'Ognevka', + }, + { + country_short: 'KZ', + region: 'Zhambyl oblysy', + city: 'Zhangatas', + }, + { + country_short: 'KZ', + region: 'Nur-Sultan', + city: 'Nur-Sultan', + }, + { + country_short: 'DE', + region: 'Schleswig-Holstein', + city: 'Norderstedt', + }, + { + country_short: 'RO', + region: 'Timis', + city: 'Timisoara', + }, + { + country_short: 'PL', + region: 'Lubelskie', + city: 'Chelm', + }, + { + country_short: 'PL', + region: 'Slaskie', + city: 'Czestochowa', + }, + { + country_short: 'PL', + region: 'Malopolskie', + city: 'Krakow', + }, + { + country_short: 'US', + region: 'Idaho', + city: 'Boise', + }, + { + country_short: 'CA', + region: 'Ontario', + city: 'Financial District', + }, + { + country_short: 'LU', + region: 'Grevenmacher', + city: 'Junglinster', + }, + { + country_short: 'AT', + region: 'Karnten', + city: 'Klagenfurt am Woerthersee', + }, + { + country_short: 'TR', + region: 'Antalya', + city: 'Antalya', + }, + { + country_short: 'ES', + region: 'Andalucia', + city: 'Olula del Rio', + }, + { + country_short: 'UA', + region: 'Ivano-Frankivska oblast', + city: 'Kalush', + }, + { + country_short: 'UA', + region: 'Donetska oblast', + city: 'Mykolaivka', + }, + { + country_short: 'GE', + region: 'Tbilisi', + city: 'Tbilisi', + }, + { + country_short: 'EE', + region: 'Harjumaa', + city: 'Tallinn', + }, + { + country_short: 'ES', + region: 'Castilla-La Mancha', + city: 'La Solana', + }, + { + country_short: 'SE', + region: 'Vastra Gotalands lan', + city: 'Anderstorp', + }, + { + country_short: 'US', + region: 'Oregon', + city: 'Tualatin', + }, + { + country_short: 'SE', + region: 'Skane lan', + city: 'Malmoe', + }, + { + country_short: 'NO', + region: 'Nordland', + city: 'Stokmarknes', + }, + { + country_short: 'NO', + region: 'Troms og Finnmark', + city: 'Hammerfest', + }, + { + country_short: 'TR', + region: 'Kahramanmaras', + city: 'Kahramanmaras', + }, + { + country_short: 'HR', + region: 'Grad Zagreb', + city: 'Odra', + }, + { + country_short: 'HR', + region: 'Istarska zupanija', + city: 'Umag', + }, + { + country_short: 'HR', + region: 'Splitsko-dalmatinska zupanija', + city: 'Kamen', + }, + { + country_short: 'MX', + region: 'Ciudad de Mexico', + city: 'Mexico City', + }, + { + country_short: 'FI', + region: 'Pohjanmaa', + city: 'Narpes', + }, + { + country_short: 'BG', + region: 'Sliven', + city: 'Kotel', + }, + { + country_short: 'RU', + region: 'Tatarstan, Respublika', + city: 'Tatarstan, Respublika', + }, + { + country_short: 'RU', + region: "Irkutskaya oblast'", + city: 'Bratsk', + }, + { + country_short: 'PL', + region: 'Zachodniopomorskie', + city: 'Szczecin', + }, + { + country_short: 'BE', + region: 'West-Vlaanderen', + city: 'Roeselare', + }, + { + country_short: 'ES', + region: 'Murcia, Region de', + city: 'Murcia, Region de', + }, + { + country_short: 'DE', + region: 'Sachsen', + city: 'Haag', + }, + { + country_short: 'HU', + region: 'Borsod-Abauj-Zemplen', + city: 'Ozd', + }, + { + country_short: 'HU', + region: 'Szabolcs-Szatmar-Bereg', + city: 'Nyiregyhaza', + }, + { + country_short: 'ES', + region: 'Canarias', + city: 'Haria', + }, + { + country_short: 'UA', + region: 'Zakarpatska oblast', + city: 'Svalyava', + }, + { + country_short: 'PL', + region: 'Swietokrzyskie', + city: 'Kostomloty Drugie', + }, + { + country_short: 'PL', + region: 'Dolnoslaskie', + city: 'Bystrzyca Klodzka', + }, + { + country_short: 'FI', + region: 'Pirkanmaa', + city: 'Tampere', + }, + { + country_short: 'FI', + region: 'Kanta-Hame', + city: 'Vantaa', + }, + { + country_short: 'FI', + region: 'Keski-Suomi', + city: 'Laukaa', + }, + { + country_short: 'PH', + region: 'Albay', + city: 'Legaspi', + }, + { + country_short: 'NL', + region: 'Utrecht', + city: 'Utrecht', + }, + { + country_short: 'RO', + region: 'Valcea', + city: 'Valcea', + }, + { + country_short: 'PL', + region: 'Lodzkie', + city: 'Lodz', + }, + { + country_short: 'RO', + region: 'Arges', + city: 'Curtea de Arges', + }, + { + country_short: 'RO', + region: 'Bihor', + city: 'Oradea', + }, + { + country_short: 'DE', + region: 'Bremen', + city: 'Bremen', + }, + { + country_short: 'SA', + region: 'Ar Riyad', + city: 'Riyadh', + }, + { + country_short: 'BE', + region: 'Oost-Vlaanderen', + city: 'Sint-Martens-Latem', + }, + { + country_short: 'UA', + region: 'Avtonomna Respublika Krym', + city: 'Morskoye', + }, + { + country_short: 'RU', + region: "Stavropol'skiy kray", + city: 'Pyatigorsk', + }, + { + country_short: 'DE', + region: 'Saarland', + city: 'Sankt Wendel', + }, + { + country_short: 'IE', + region: 'Kerry', + city: 'Tralee', + }, + { + country_short: 'IE', + region: 'Limerick', + city: 'Abbeyfeale', + }, + { + country_short: 'TR', + region: 'Gaziantep', + city: 'Gaziantep', + }, + { + country_short: 'MT', + region: 'Birkirkara', + city: 'Birkirkara', + }, + { + country_short: 'NL', + region: 'Limburg', + city: 'Beek', + }, + { + country_short: 'LT', + region: 'Vilniaus apskritis', + city: 'Vilnius', + }, + { + country_short: 'HU', + region: 'Bacs-Kiskun', + city: 'Bacsalmas', + }, + { + country_short: 'HU', + region: 'Nograd', + city: 'Retsag', + }, + { + country_short: 'SE', + region: 'Kronobergs lan', + city: 'Vaxjo', + }, + { + country_short: 'RU', + region: "Novgorodskaya oblast'", + city: 'Velikiy Novgorod', + }, + { + country_short: 'RU', + region: "Tyumenskaya oblast'", + city: "Tyumen'", + }, + { + country_short: 'RU', + region: "Omskaya oblast'", + city: "Isil'kul'", + }, + { + country_short: 'RU', + region: 'Altayskiy kray', + city: 'Bayevo', + }, + { + country_short: 'RU', + region: 'Khakasiya, Respublika', + city: 'Khakasiya, Respublika', + }, + { + country_short: 'RU', + region: 'Krasnoyarskiy kray', + city: 'Uzhur', + }, + { + country_short: 'RU', + region: "Kemerovskaya oblast'", + city: 'Novokuznetsk', + }, + { + country_short: 'RU', + region: "Astrakhanskaya oblast'", + city: "Astrakhan'", + }, + { + country_short: 'RU', + region: "Kaluzhskaya oblast'", + city: 'Kaluga', + }, + { + country_short: 'RU', + region: 'Permskiy kray', + city: 'Perm', + }, + { + country_short: 'RU', + region: 'Mariy El, Respublika', + city: 'Mariy El, Respublika', + }, + { + country_short: 'RU', + region: 'Chuvashskaya Respublika', + city: 'Cheboksary', + }, + { + country_short: 'RU', + region: 'Yamalo-Nenetskiy avtonomnyy okrug', + city: 'Noyabrsk', + }, + { + country_short: 'RU', + region: "Tul'skaya oblast'", + city: 'Tula', + }, + { + country_short: 'RU', + region: 'Primorskiy kray', + city: "Ol'ga", + }, + { + country_short: 'RU', + region: "Penzenskaya oblast'", + city: 'Penza', + }, + { + country_short: 'RU', + region: "Kurganskaya oblast'", + city: 'Kurgan', + }, + { + country_short: 'RU', + region: "Chelyabinskaya oblast'", + city: 'Chelyabinsk', + }, + { + country_short: 'SE', + region: 'Ostergotlands lan', + city: 'Norrkoping', + }, + { + country_short: 'SE', + region: 'Blekinge lan', + city: 'Kallinge', + }, + { + country_short: 'SE', + region: 'Kalmar lan', + city: 'Virserum', + }, + { + country_short: 'SE', + region: 'Dalarnas lan', + city: 'Horndal', + }, + { + country_short: 'SE', + region: 'Hallands lan', + city: 'Falkenberg', + }, + { + country_short: 'SE', + region: 'Vastmanlands lan', + city: 'Surahammar', + }, + { + country_short: 'SE', + region: 'Jonkopings lan', + city: 'Habo', + }, + { + country_short: 'SE', + region: 'Orebro lan', + city: 'Orebro', + }, + { + country_short: 'SE', + region: 'Sodermanlands lan', + city: 'Katrineholm', + }, + { + country_short: 'SE', + region: 'Gavleborgs lan', + city: 'Soderhamn', + }, + { + country_short: 'SE', + region: 'Vasterbottens lan', + city: 'Umea', + }, + { + country_short: 'SE', + region: 'Jamtlands lan', + city: 'Ostersund', + }, + { + country_short: 'SE', + region: 'Gotlands lan', + city: 'Visby', + }, + { + country_short: 'SE', + region: 'Vasternorrlands lan', + city: 'Kramfors', + }, + { + country_short: 'SE', + region: 'Norrbottens lan', + city: 'Pitea', + }, + { + country_short: 'KZ', + region: 'Almaty', + city: 'Almaty', + }, + { + country_short: 'KZ', + region: 'Almaty oblysy', + city: 'Alga', + }, + { + country_short: 'KZ', + region: 'Ongtustik Qazaqstan oblysy', + city: 'Aksu', + }, + { + country_short: 'KZ', + region: 'Shymkent', + city: 'Shymkent', + }, + { + country_short: 'KZ', + region: 'Aqmola oblysy', + city: 'Zhaqsy', + }, + { + country_short: 'KZ', + region: 'Batys Qazaqstan oblysy', + city: 'Fedorovka', + }, + { + country_short: 'KZ', + region: 'Qyzylorda oblysy', + city: 'Aral', + }, + { + country_short: 'KZ', + region: 'Qaraghandy oblysy', + city: 'Kazakhstan', + }, + { + country_short: 'KZ', + region: 'Soltustik Qazaqstan oblysy', + city: 'Petropavl', + }, + { + country_short: 'KZ', + region: 'Qostanay oblysy', + city: 'Rudnyy', + }, + { + country_short: 'KZ', + region: 'Atyrau oblysy', + city: 'Atyrau', + }, + { + country_short: 'KZ', + region: 'Pavlodar oblysy', + city: 'Pavlodar', + }, + { + country_short: 'KZ', + region: 'Aqtobe oblysy', + city: 'Shubarshi', + }, + { + country_short: 'KZ', + region: 'Mangghystau oblysy', + city: 'Omirzaq', + }, + { + country_short: 'PT', + region: 'Guarda', + city: 'Pinhel', + }, + { + country_short: 'PT', + region: 'Viseu', + city: 'Viseu', + }, + { + country_short: 'PT', + region: 'Aveiro', + city: 'Espinho', + }, + { + country_short: 'PT', + region: 'Porto', + city: 'Porto', + }, + { + country_short: 'PT', + region: 'Vila Real', + city: 'Chaves', + }, + { + country_short: 'PT', + region: 'Castelo Branco', + city: 'Castelo Branco', + }, + { + country_short: 'PT', + region: 'Coimbra', + city: 'Quinta', + }, + { + country_short: 'PT', + region: 'Leiria', + city: 'Leiria', + }, + { + country_short: 'PT', + region: 'Portalegre', + city: 'Alter do Chao', + }, + { + country_short: 'PT', + region: 'Braga', + city: 'Ribeirao', + }, + { + country_short: 'PT', + region: 'Santarem', + city: 'Monsanto', + }, + { + country_short: 'PT', + region: 'Viana do Castelo', + city: 'Mozelos', + }, + { + country_short: 'PT', + region: 'Braganca', + city: 'Braganca', + }, + { + country_short: 'PT', + region: 'Faro', + city: 'Monte Gordo', + }, + { + country_short: 'GR', + region: 'Kriti', + city: 'Chania', + }, + { + country_short: 'GR', + region: 'Peloponnisos', + city: 'Vytina', + }, + { + country_short: 'GR', + region: 'Sterea Ellada', + city: 'Itea', + }, + { + country_short: 'GR', + region: 'Voreio Aigaio', + city: 'Chios', + }, + { + country_short: 'GR', + region: 'Notio Aigaio', + city: 'Ziparion', + }, + { + country_short: 'GR', + region: 'Dytiki Ellada', + city: 'Mirsini', + }, + { + country_short: 'GR', + region: 'Thessalia', + city: 'Artesianon', + }, + { + country_short: 'GR', + region: 'Ipeiros', + city: 'Kanallakion', + }, + { + country_short: 'GR', + region: 'Kentriki Makedonia', + city: 'Portaria', + }, + { + country_short: 'GR', + region: 'Anatoliki Makedonia kai Thraki', + city: 'Volax', + }, + { + country_short: 'GR', + region: 'Ionia Nisia', + city: 'Kerkyra', + }, + { + country_short: 'GR', + region: 'Dytiki Makedonia', + city: 'Grevena', + }, + { + country_short: 'SA', + region: 'Makkah al Mukarramah', + city: "Ta'if", + }, + { + country_short: 'SA', + region: "Ha'il", + city: "Ha'il", + }, + { + country_short: 'SA', + region: 'Al Qasim', + city: 'Al Bukayriyah', + }, + { + country_short: 'SA', + region: 'Ash Sharqiyah', + city: 'Dhahran', + }, + { + country_short: 'SA', + region: 'Al Madinah al Munawwarah', + city: 'Medina', + }, + { + country_short: 'SA', + region: 'Al Jawf', + city: 'Sakaka', + }, + { + country_short: 'SA', + region: "'Asir", + city: 'Tanumah', + }, + { + country_short: 'SA', + region: 'Tabuk', + city: 'Tabuk', + }, + { + country_short: 'SA', + region: 'Al Hudud ash Shamaliyah', + city: 'Turayf', + }, + { + country_short: 'RU', + region: "Ivanovskaya oblast'", + city: 'Ivanovo', + }, + { + country_short: 'RU', + region: "Volgogradskaya oblast'", + city: 'Volgograd', + }, + { + country_short: 'RU', + region: "Vologodskaya oblast'", + city: 'Vologda', + }, + { + country_short: 'RU', + region: "Samarskaya oblast'", + city: 'Tolyatti', + }, + { + country_short: 'RU', + region: "Ul'yanovskaya oblast'", + city: 'Ulyanovsk', + }, + { + country_short: 'RU', + region: 'Adygeya, Respublika', + city: 'Adygeya, Respublika', + }, + { + country_short: 'RU', + region: "Rostovskaya oblast'", + city: 'Rostov-na-Donu', + }, + { + country_short: 'RU', + region: "Saratovskaya oblast'", + city: 'Sokolovyy', + }, + { + country_short: 'RU', + region: "Arkhangel'skaya oblast'", + city: "Arkhangel'sk", + }, + { + country_short: 'RU', + region: 'Khabarovskiy kray', + city: 'Khabarovsk', + }, + { + country_short: 'RU', + region: "Bryanskaya oblast'", + city: 'Bryansk', + }, + { + country_short: 'RU', + region: "Smolenskaya oblast'", + city: 'Smolensk', + }, + { + country_short: 'RU', + region: "Yaroslavskaya oblast'", + city: "Yaroslavl'", + }, + { + country_short: 'RU', + region: "Orlovskaya oblast'", + city: 'Orel', + }, + { + country_short: 'RU', + region: "Kurskaya oblast'", + city: 'Kurchatov', + }, + { + country_short: 'RU', + region: "Tverskaya oblast'", + city: "Tver'", + }, + { + country_short: 'RU', + region: "Kostromskaya oblast'", + city: 'Kostroma', + }, + { + country_short: 'RU', + region: "Lipetskaya oblast'", + city: 'Lipetsk', + }, + { + country_short: 'RU', + region: "Leningradskaya oblast'", + city: 'Priladozhskiy', + }, + { + country_short: 'DK', + region: 'Sjaelland', + city: 'Korsor', + }, + { + country_short: 'DK', + region: 'Syddanmark', + city: 'Oksbol', + }, + { + country_short: 'DK', + region: 'Nordjylland', + city: 'Frederikshavn', + }, + { + country_short: 'CY', + region: 'Lefkosia', + city: 'Nicosia', + }, + { + country_short: 'ES', + region: 'Navarra, Comunidad Foral de', + city: 'Navarra, Comunidad Foral de', + }, + { + country_short: 'ES', + region: 'Pais Vasco', + city: 'San Sebastian', + }, + { + country_short: 'ES', + region: 'Galicia', + city: 'Lugo', + }, + { + country_short: 'ES', + region: 'Castilla y Leon', + city: 'Burgos', + }, + { + country_short: 'ES', + region: 'Illes Balears', + city: 'Palma', + }, + { + country_short: 'ES', + region: 'Aragon', + city: 'Daroca', + }, + { + country_short: 'ES', + region: 'Cantabria', + city: 'Reinosa', + }, + { + country_short: 'ES', + region: 'La Rioja', + city: 'Logrono', + }, + { + country_short: 'ES', + region: 'Melilla', + city: 'Melilla', + }, + { + country_short: 'ES', + region: 'Asturias, Principado de', + city: 'Asturias, Principado de', + }, + { + country_short: 'ES', + region: 'Ceuta', + city: 'Ceuta', + }, + { + country_short: 'IR', + region: 'Tehran', + city: 'Tehran', + }, + { + country_short: 'IR', + region: 'Sistan va Baluchestan', + city: 'Zabol', + }, + { + country_short: 'IR', + region: 'Hamadan', + city: 'Hamadan', + }, + { + country_short: 'IR', + region: 'Fars', + city: 'Shiraz', + }, + { + country_short: 'IR', + region: 'Khorasan-e Razavi', + city: 'Quchan', + }, + { + country_short: 'IR', + region: 'Khuzestan', + city: 'Ahvaz', + }, + { + country_short: 'IR', + region: 'Alborz', + city: 'Karaj', + }, + { + country_short: 'IR', + region: 'Azarbayjan-e Sharqi', + city: 'Tabriz', + }, + { + country_short: 'IR', + region: 'Mazandaran', + city: 'Tonekabon', + }, + { + country_short: 'IR', + region: 'Qazvin', + city: 'Kaman', + }, + { + country_short: 'IR', + region: 'Markazi', + city: 'Abyek', + }, + { + country_short: 'NO', + region: 'Viken', + city: 'Vikersund', + }, + { + country_short: 'NO', + region: 'Vestland', + city: 'Hylkje', + }, + { + country_short: 'NO', + region: 'Trondelag', + city: 'Mebonden', + }, + { + country_short: 'NO', + region: 'Innlandet', + city: 'Loten', + }, + { + country_short: 'NO', + region: 'Agder', + city: 'Birketveit', + }, + { + country_short: 'NO', + region: 'Rogaland', + city: 'Stavanger', + }, + { + country_short: 'NO', + region: 'More og Romsdal', + city: 'Alesund', + }, + { + country_short: 'DE', + region: 'Niedersachsen', + city: 'Grewenweise', + }, + { + country_short: 'DE', + region: 'Sachsen-Anhalt', + city: 'Halle (Saale)', + }, + { + country_short: 'DE', + region: 'Mecklenburg-Vorpommern', + city: 'Grun Kordshagen', + }, + { + country_short: 'DE', + region: 'Thuringen', + city: 'Gorsbach', + }, + { + country_short: 'DE', + region: 'Brandenburg', + city: 'Bernau bei Berlin', + }, + { + country_short: 'IR', + region: 'Lorestan', + city: 'Khorramabad', + }, + { + country_short: 'IR', + region: 'Azarbayjan-e Gharbi', + city: 'Azad', + }, + { + country_short: 'IR', + region: 'Qom', + city: 'Qom', + }, + { + country_short: 'IR', + region: 'Hormozgan', + city: 'Bandar Abbas', + }, + { + country_short: 'IR', + region: 'Golestan', + city: 'Gorgan', + }, + { + country_short: 'IR', + region: 'Yazd', + city: 'Yazd', + }, + { + country_short: 'IR', + region: 'Kerman', + city: 'Golestan', + }, + { + country_short: 'IR', + region: 'Khorasan-e Shomali', + city: 'Esfarayen', + }, + { + country_short: 'IR', + region: 'Kordestan', + city: 'Khorasan', + }, + { + country_short: 'IR', + region: 'Semnan', + city: 'Semnan', + }, + { + country_short: 'IR', + region: 'Khorasan-e Jonubi', + city: 'Birjand', + }, + { + country_short: 'IR', + region: 'Esfahan', + city: 'Sistan', + }, + { + country_short: 'IR', + region: 'Gilan', + city: 'Astaneh-ye Ashrafiyeh', + }, + { + country_short: 'IR', + region: 'Zanjan', + city: 'Aras', + }, + { + country_short: 'IR', + region: 'Kermanshah', + city: 'Kermanshah', + }, + { + country_short: 'IR', + region: 'Ilam', + city: 'Ilam', + }, + { + country_short: 'IR', + region: 'Chahar Mahal va Bakhtiari', + city: 'Borujen', + }, + { + country_short: 'IR', + region: 'Bushehr', + city: 'Sena', + }, + { + country_short: 'IR', + region: 'Ardabil', + city: 'Parsabad', + }, + { + country_short: 'IR', + region: 'Kohgiluyeh va Bowyer Ahmad', + city: 'Dehdasht', + }, + { + country_short: 'US', + region: 'Montana', + city: 'Havre', + }, + { + country_short: 'HU', + region: 'Budapest', + city: 'Budapest', + }, + { + country_short: 'US', + region: 'Georgia', + city: 'Atlanta', + }, + { + country_short: 'US', + region: 'Missouri', + city: 'Kansas City', + }, + { + country_short: 'US', + region: 'Massachusetts', + city: 'Boston', + }, + { + country_short: 'CA', + region: 'Quebec', + city: 'Montreal', + }, + { + country_short: 'US', + region: 'Nebraska', + city: 'Omaha', + }, + { + country_short: 'US', + region: 'Tennessee', + city: 'Nashville', + }, + { + country_short: 'US', + region: 'Pennsylvania', + city: 'Pittsburgh', + }, + { + country_short: 'ID', + region: 'Sulawesi Utara', + city: 'Manado', + }, + { + country_short: 'LT', + region: 'Kauno apskritis', + city: 'Kaunas', + }, + { + country_short: 'US', + region: 'Michigan', + city: 'Detroit', + }, + { + country_short: 'US', + region: 'Colorado', + city: 'Denver', + }, + { + country_short: 'RS', + region: 'Beograd', + city: 'Belgrade', + }, + { + country_short: 'MX', + region: 'Queretaro', + city: 'Queretaro', + }, + { + country_short: 'PL', + region: 'Wielkopolskie', + city: 'Poznan', + }, + { + country_short: 'US', + region: 'Arizona', + city: 'Phoenix', + }, + { + country_short: 'BH', + region: "Al 'Asimah", + city: 'Manama', + }, + { + country_short: 'ID', + region: 'Jakarta Raya', + city: 'Jakarta', + }, + { + country_short: 'CA', + region: 'Alberta', + city: 'Calgary', + }, + { + country_short: 'US', + region: 'Indiana', + city: 'Indianapolis', + }, + { + country_short: 'US', + region: 'Mississippi', + city: 'Jackson', + }, + { + country_short: 'US', + region: 'Louisiana', + city: 'Monroe', + }, + { + country_short: 'US', + region: 'Minnesota', + city: 'Minneapolis', + }, + { + country_short: 'US', + region: 'Connecticut', + city: 'Hartford', + }, + { + country_short: 'US', + region: 'Hawaii', + city: 'Honolulu', + }, + { + country_short: 'VU', + region: 'Tafea', + city: 'Isangel', + }, + { + country_short: 'US', + region: 'Oklahoma', + city: 'Tulsa', + }, + { + country_short: 'US', + region: 'Wisconsin', + city: 'Milwaukee', + }, + { + country_short: 'US', + region: 'Nevada', + city: 'Las Vegas', + }, + { + country_short: 'US', + region: 'New Mexico', + city: 'Albuquerque', + }, + { + country_short: 'US', + region: 'Iowa', + city: 'Leighton', + }, + { + country_short: 'US', + region: 'New Hampshire', + city: 'Lebanon', + }, + { + country_short: 'US', + region: 'Rhode Island', + city: 'Providence', + }, + { + country_short: 'US', + region: 'Kansas', + city: 'Wichita', + }, + { + country_short: 'US', + region: 'Arkansas', + city: 'Little Rock', + }, + { + country_short: 'US', + region: 'South Carolina', + city: 'Easley', + }, + { + country_short: 'US', + region: 'Alabama', + city: 'Birmingham', + }, + { + country_short: 'CA', + region: 'Saskatchewan', + city: 'Saskatoon', + }, + { + country_short: 'PE', + region: 'Lima', + city: 'Lima', + }, + { + country_short: 'US', + region: 'Vermont', + city: 'Burlington', + }, + { + country_short: 'CA', + region: 'Nova Scotia', + city: 'Bedford', + }, + { + country_short: 'MX', + region: 'Nuevo Leon', + city: 'Monterrey', + }, + { + country_short: 'US', + region: 'South Dakota', + city: 'Rapid City', + }, + { + country_short: 'CA', + region: 'New Brunswick', + city: 'Saint John', + }, + { + country_short: 'UY', + region: 'Salto', + city: 'Salto', + }, + { + country_short: 'US', + region: 'Maine', + city: 'Rumford', + }, + { + country_short: 'US', + region: 'Alaska', + city: 'Anchorage', + }, + { + country_short: 'CA', + region: 'Prince Edward Island', + city: 'Charlottetown', + }, + { + country_short: 'CO', + region: 'Distrito Capital de Bogota', + city: 'Bogota', + }, + { + country_short: 'BM', + region: 'Hamilton', + city: 'Hamilton', + }, + { + country_short: 'SI', + region: 'Ljubljana', + city: 'Ljubljana', + }, + { + country_short: 'DJ', + region: 'Djibouti', + city: 'Djibouti', + }, + { + country_short: 'PA', + region: 'Panama', + city: 'Panama', + }, + { + country_short: 'CN', + region: 'Xizang', + city: 'Gar', + }, + { + country_short: 'CH', + region: 'Geneve', + city: 'Geneva', + }, + { + country_short: 'SY', + region: 'Rif Dimashq', + city: "'Ayn al Fijah", + }, + { + country_short: 'SY', + region: 'Halab', + city: 'Aleppo', + }, + { + country_short: 'SY', + region: 'Dimashq', + city: 'Damascus', + }, + { + country_short: 'SY', + region: 'Hims', + city: 'Homs', + }, + { + country_short: 'SY', + region: "As Suwayda'", + city: 'As Suwayda', + }, + { + country_short: 'SY', + region: 'Hamah', + city: 'Masyaf', + }, + { + country_short: 'SY', + region: "Dar'a", + city: "Dar'a", + }, + { + country_short: 'SY', + region: 'Al Ladhiqiyah', + city: 'Latakia', + }, + { + country_short: 'SY', + region: 'Tartus', + city: 'Hamidiyah', + }, + { + country_short: 'SY', + region: 'Al Qunaytirah', + city: 'Khan Arnabah', + }, + { + country_short: 'SY', + region: 'Dayr az Zawr', + city: 'Dayr az Zawr', + }, + { + country_short: 'UA', + region: 'Vinnytska oblast', + city: 'Kozyatyn', + }, + { + country_short: 'UA', + region: 'Volynska oblast', + city: 'Lutsk', + }, + { + country_short: 'UA', + region: 'Mykolaivska oblast', + city: 'Ochakiv', + }, + { + country_short: 'UA', + region: 'Chernihivska oblast', + city: 'Nizhyn', + }, + { + country_short: 'UA', + region: 'Kirovohradska oblast', + city: "Svitlovods'k", + }, + { + country_short: 'UA', + region: 'Khmelnytska oblast', + city: 'Shepetivka', + }, + { + country_short: 'UA', + region: 'Dnipropetrovska oblast', + city: 'Kryvyy Rih', + }, + { + country_short: 'LT', + region: 'Telsiu apskritis', + city: 'Telsiai', + }, + { + country_short: 'RU', + region: 'Saha, Respublika', + city: 'Saha, Respublika', + }, + { + country_short: 'CZ', + region: 'Karlovarsky kraj', + city: 'Sokolov', + }, + { + country_short: 'CH', + region: 'Vaud', + city: 'Crissier', + }, + { + country_short: 'NL', + region: 'Groningen', + city: 'Korrewegwijk', + }, + { + country_short: 'RO', + region: 'Ilfov', + city: 'Chiajna', + }, + { + country_short: 'RO', + region: 'Vrancea', + city: 'Focsani', + }, + { + country_short: 'RO', + region: 'Arad', + city: 'Arad', + }, + { + country_short: 'RO', + region: 'Alba', + city: 'Alba Iulia', + }, + { + country_short: 'RO', + region: 'Bistrita-Nasaud', + city: 'Bistrita', + }, + { + country_short: 'RO', + region: 'Vaslui', + city: 'Vaslui', + }, + { + country_short: 'RO', + region: 'Mures', + city: 'Iernut', + }, + { + country_short: 'RO', + region: 'Salaj', + city: 'Zalau', + }, + { + country_short: 'RO', + region: 'Prahova', + city: 'Ploiesti', + }, + { + country_short: 'RO', + region: 'Iasi', + city: 'Iasi', + }, + { + country_short: 'RO', + region: 'Cluj', + city: 'Cluj-Napoca', + }, + { + country_short: 'RO', + region: 'Galati', + city: 'Galati', + }, + { + country_short: 'RO', + region: 'Braila', + city: 'Rosiori', + }, + { + country_short: 'RO', + region: 'Constanta', + city: 'Constanta', + }, + { + country_short: 'RO', + region: 'Mehedinti', + city: 'Douazeci si Trei August', + }, + { + country_short: 'RO', + region: 'Sibiu', + city: 'Sibiu', + }, + { + country_short: 'RO', + region: 'Harghita', + city: 'Tulghes', + }, + { + country_short: 'RO', + region: 'Ialomita', + city: 'Ciulnita', + }, + { + country_short: 'RO', + region: 'Teleorman', + city: 'Traian', + }, + { + country_short: 'RO', + region: 'Buzau', + city: 'Buzau', + }, + { + country_short: 'RO', + region: 'Tulcea', + city: 'Tulcea', + }, + { + country_short: 'RO', + region: 'Calarasi', + city: 'Calarasi', + }, + { + country_short: 'RO', + region: 'Dambovita', + city: 'Produlesti', + }, + { + country_short: 'RO', + region: 'Bacau', + city: 'Onesti', + }, + { + country_short: 'RU', + region: 'Altay, Respublika', + city: 'Altay, Respublika', + }, + { + country_short: 'RU', + region: "Murmanskaya oblast'", + city: 'Murmansk', + }, + { + country_short: 'RU', + region: "Orenburgskaya oblast'", + city: 'Orsk', + }, + { + country_short: 'RU', + region: 'Udmurtskaya Respublika', + city: 'Izhevsk', + }, + { + country_short: 'RU', + region: "Tambovskaya oblast'", + city: 'Sosnovka', + }, + { + country_short: 'RU', + region: "Belgorodskaya oblast'", + city: 'Belgorod', + }, + { + country_short: 'LB', + region: 'Beyrouth', + city: 'Beirut', + }, + { + country_short: 'LB', + region: 'Beqaa', + city: 'Zahle', + }, + { + country_short: 'IQ', + region: 'As Sulaymaniyah', + city: 'Halabjah', + }, + { + country_short: 'IQ', + region: 'Arbil', + city: 'Kuysanjaq', + }, + { + country_short: 'GE', + region: 'Guria', + city: 'Meria', + }, + { + country_short: 'AZ', + region: 'Baki', + city: 'Baku', + }, + { + country_short: 'ZW', + region: 'Harare', + city: 'Harare', + }, + { + country_short: 'CD', + region: 'Kinshasa', + city: 'Kinshasa', + }, + { + country_short: 'IQ', + region: 'An Najaf', + city: 'Najaf', + }, + { + country_short: 'IQ', + region: 'Ninawa', + city: 'Mosul', + }, + { + country_short: 'IQ', + region: 'Kirkuk', + city: 'Kirkuk', + }, + { + country_short: 'RU', + region: 'Severnaya Osetiya, Respublika', + city: 'Severnaya Osetiya, Respublika', + }, + { + country_short: 'TR', + region: 'Kutahya', + city: 'Kutahya', + }, + { + country_short: 'TR', + region: 'Canakkale', + city: 'Kepez', + }, + { + country_short: 'TR', + region: 'Mersin', + city: 'Anamur', + }, + { + country_short: 'TR', + region: 'Manisa', + city: 'Manisa', + }, + { + country_short: 'TR', + region: 'Adana', + city: 'Adana', + }, + { + country_short: 'TR', + region: 'Nigde', + city: 'Ulukisla', + }, + { + country_short: 'RO', + region: 'Giurgiu', + city: 'Gaiseni', + }, + { + country_short: 'RO', + region: 'Neamt', + city: 'Ungureni', + }, + { + country_short: 'RO', + region: 'Covasna', + city: 'Tirgu Ocna', + }, + { + country_short: 'RO', + region: 'Dolj', + city: 'Craiova', + }, + { + country_short: 'RO', + region: 'Caras-Severin', + city: 'Resita', + }, + { + country_short: 'RO', + region: 'Olt', + city: 'Slatina', + }, + { + country_short: 'RO', + region: 'Maramures', + city: 'Seini', + }, + { + country_short: 'LT', + region: 'Utenos apskritis', + city: 'Visaginas', + }, + { + country_short: 'LT', + region: 'Marijampoles apskritis', + city: 'Marijampole', + }, + { + country_short: 'LT', + region: 'Alytaus apskritis', + city: 'Alytus', + }, + { + country_short: 'LT', + region: 'Klaipedos apskritis', + city: 'Silute', + }, + { + country_short: 'OM', + region: 'Masqat', + city: 'Muscat', + }, + { + country_short: 'OM', + region: 'Zufar', + city: 'Salalah', + }, + { + country_short: 'OM', + region: 'Al Wusta', + city: 'Sawqarah', + }, + { + country_short: 'OM', + region: 'Ad Dakhiliyah', + city: 'Ar Raddah', + }, + { + country_short: 'OM', + region: 'Shamal al Batinah', + city: 'As Suwayq', + }, + { + country_short: 'OM', + region: 'Janub ash Sharqiyah', + city: 'Sur', + }, + { + country_short: 'OM', + region: 'Az Zahirah', + city: "'Ibri", + }, + { + country_short: 'OM', + region: 'Janub al Batinah', + city: "Barka'", + }, + { + country_short: 'OM', + region: 'Shamal ash Sharqiyah', + city: "Ibra'", + }, + { + country_short: 'SK', + region: 'Nitriansky kraj', + city: 'Zlate Moravce', + }, + { + country_short: 'RS', + region: 'Nisavski okrug', + city: 'Nis', + }, + { + country_short: 'NL', + region: 'Fryslan', + city: 'Sneek', + }, + { + country_short: 'IS', + region: 'Sudurland', + city: 'Selfoss', + }, + { + country_short: 'IS', + region: 'Sudurnes', + city: 'Njardvik', + }, + { + country_short: 'IS', + region: 'Austurland', + city: 'Neskaupstadur', + }, + { + country_short: 'RU', + region: "Ryazanskaya oblast'", + city: "Ryazan'", + }, + { + country_short: 'BE', + region: 'Vlaams-Brabant', + city: 'Landen', + }, + { + country_short: 'BE', + region: 'Luxembourg', + city: 'Neufchateau', + }, + { + country_short: 'BE', + region: 'Namur', + city: 'Beauraing', + }, + { + country_short: 'BE', + region: 'Hainaut', + city: 'La Louviere', + }, + { + country_short: 'BE', + region: 'Brabant wallon', + city: 'Tubize', + }, + { + country_short: 'TR', + region: 'Elazig', + city: 'Sivrice', + }, + { + country_short: 'TR', + region: 'Bingol', + city: 'Bingol', + }, + { + country_short: 'TR', + region: 'Sanliurfa', + city: 'Sanliurfa', + }, + { + country_short: 'TR', + region: 'Mardin', + city: 'Mardin', + }, + { + country_short: 'TR', + region: 'Diyarbakir', + city: 'Yuksek', + }, + { + country_short: 'TR', + region: 'Kocaeli', + city: 'Gebze', + }, + { + country_short: 'TR', + region: 'Sakarya', + city: 'Adapazari', + }, + { + country_short: 'TR', + region: 'Yalova', + city: 'Kadikoy', + }, + { + country_short: 'TR', + region: 'Mugla', + city: 'Dalaman', + }, + { + country_short: 'TR', + region: 'Usak', + city: 'Sivasli', + }, + { + country_short: 'TR', + region: 'Isparta', + city: 'Isparta', + }, + { + country_short: 'TR', + region: 'Burdur', + city: 'Bucak', + }, + { + country_short: 'TR', + region: 'Denizli', + city: 'Denizli', + }, + { + country_short: 'TR', + region: 'Balikesir', + city: 'Susurluk', + }, + { + country_short: 'TR', + region: 'Aydin', + city: 'Didim', + }, + { + country_short: 'TR', + region: 'Sinop', + city: 'Sinop', + }, + { + country_short: 'TR', + region: 'Rize', + city: 'Rize', + }, + { + country_short: 'TR', + region: 'Konya', + city: 'Konya', + }, + { + country_short: 'TR', + region: 'Karaman', + city: 'Karaman', + }, + { + country_short: 'TR', + region: 'Ordu', + city: 'Fatsa', + }, + { + country_short: 'TR', + region: 'Kayseri', + city: 'Incesu', + }, + { + country_short: 'TR', + region: 'Trabzon', + city: 'Trabzon', + }, + { + country_short: 'TR', + region: 'Adiyaman', + city: 'Adiyaman', + }, + { + country_short: 'TR', + region: 'Gumushane', + city: 'Kelkit', + }, + { + country_short: 'TR', + region: 'Artvin', + city: 'Artvin', + }, + { + country_short: 'TR', + region: 'Bolu', + city: 'Gerede', + }, + { + country_short: 'TR', + region: 'Kastamonu', + city: 'Devrekani', + }, + { + country_short: 'TR', + region: 'Aksaray', + city: 'Eskil', + }, + { + country_short: 'TR', + region: 'Van', + city: 'Ercis', + }, + { + country_short: 'TR', + region: 'Giresun', + city: 'Camoluk', + }, + { + country_short: 'TR', + region: 'Duzce', + city: 'Golyaka', + }, + { + country_short: 'TR', + region: 'Cankiri', + city: 'Cankiri', + }, + { + country_short: 'TR', + region: 'Samsun', + city: 'Bafra', + }, + { + country_short: 'TR', + region: 'Erzurum', + city: 'Erzurum', + }, + { + country_short: 'TR', + region: 'Kars', + city: 'Sarikamis', + }, + { + country_short: 'TR', + region: 'Corum', + city: 'Corum', + }, + { + country_short: 'TR', + region: 'Kirsehir', + city: 'Kirsehir', + }, + { + country_short: 'TR', + region: 'Afyonkarahisar', + city: 'Sandikli', + }, + { + country_short: 'TR', + region: 'Eskisehir', + city: 'Eskisehir', + }, + { + country_short: 'TR', + region: 'Hatay', + city: 'Antioch', + }, + { + country_short: 'TR', + region: 'Hakkari', + city: 'Semdinli', + }, + { + country_short: 'TR', + region: 'Malatya', + city: 'Malatya', + }, + { + country_short: 'TR', + region: 'Tekirdag', + city: 'Muratli', + }, + { + country_short: 'TR', + region: 'Agri', + city: 'Agri', + }, + { + country_short: 'TR', + region: 'Edirne', + city: 'Kesan', + }, + { + country_short: 'TR', + region: 'Kirikkale', + city: 'Kirikkale', + }, + { + country_short: 'TR', + region: 'Yozgat', + city: 'Yozgat', + }, + { + country_short: 'TR', + region: 'Zonguldak', + city: 'Guluc', + }, + { + country_short: 'TR', + region: 'Sirnak', + city: 'Bozalan', + }, + { + country_short: 'TR', + region: 'Bilecik', + city: 'Osmaneli', + }, + { + country_short: 'BG', + region: 'Kyustendil', + city: 'Dupnitsa', + }, + { + country_short: 'BG', + region: 'Kardzhali', + city: 'Kardzhali', + }, + { + country_short: 'SI', + region: 'Velenje', + city: 'Velenje', + }, + { + country_short: 'SI', + region: 'Domzale', + city: 'Domzale', + }, + { + country_short: 'MK', + region: 'Bitola', + city: 'Bitola', + }, + { + country_short: 'MK', + region: 'Struga', + city: 'Struga', + }, + { + country_short: 'MK', + region: 'Centar', + city: 'Skopje', + }, + { + country_short: 'MK', + region: 'Debar', + city: 'Debar', + }, + { + country_short: 'LI', + region: 'Mauren', + city: 'Mauren', + }, + { + country_short: 'LI', + region: 'Schaan', + city: 'Schaan', + }, + { + country_short: 'LI', + region: 'Vaduz', + city: 'Vaduz', + }, + { + country_short: 'LI', + region: 'Ruggell', + city: 'Ruggell', + }, + { + country_short: 'LI', + region: 'Gamprin', + city: 'Gamprin', + }, + { + country_short: 'LI', + region: 'Triesen', + city: 'Triesen', + }, + { + country_short: 'LI', + region: 'Balzers', + city: 'Balzers', + }, + { + country_short: 'JE', + region: 'Jersey', + city: 'Saint Helier', + }, + { + country_short: 'OM', + region: 'Al Buraymi', + city: 'Al Buraymi', + }, + { + country_short: 'OM', + region: 'Musandam', + city: 'Khasab', + }, + { + country_short: 'HU', + region: 'Csongrad-Csanad', + city: 'Szentes', + }, + { + country_short: 'HU', + region: 'Pest', + city: 'Toeroekbalint', + }, + { + country_short: 'HU', + region: 'Veszprem', + city: 'Tapolca', + }, + { + country_short: 'HR', + region: 'Krapinsko-zagorska zupanija', + city: 'Zabok', + }, + { + country_short: 'HR', + region: 'Bjelovarsko-bilogorska zupanija', + city: 'Garesnica', + }, + { + country_short: 'HR', + region: 'Karlovacka zupanija', + city: 'Ostarije', + }, + { + country_short: 'HR', + region: 'Vukovarsko-srijemska zupanija', + city: 'Rokovci', + }, + { + country_short: 'HR', + region: 'Koprivnicko-krizevacka zupanija', + city: 'Koprivnica', + }, + { + country_short: 'HR', + region: 'Brodsko-posavska zupanija', + city: 'Davor', + }, + { + country_short: 'HR', + region: 'Primorsko-goranska zupanija', + city: 'Krk', + }, + { + country_short: 'HR', + region: 'Zagrebacka zupanija', + city: 'Samobor', + }, + { + country_short: 'CZ', + region: 'Stredocesky kraj', + city: 'Jirny', + }, + { + country_short: 'CZ', + region: 'Kraj Vysocina', + city: 'Pelhrimov', + }, + { + country_short: 'CZ', + region: 'Moravskoslezsky kraj', + city: 'Ostrava', + }, + { + country_short: 'CZ', + region: 'Ustecky kraj', + city: 'Chomutov', + }, + { + country_short: 'SA', + region: 'Jazan', + city: 'Mizhirah', + }, + { + country_short: 'SA', + region: 'Al Bahah', + city: 'Al Bahah', + }, + { + country_short: 'SA', + region: 'Najran', + city: 'Najran', + }, + { + country_short: 'IE', + region: 'Kildare', + city: 'Naas', + }, + { + country_short: 'PT', + region: 'Setubal', + city: 'Santo Andre', + }, + { + country_short: 'PT', + region: 'Evora', + city: 'Viana do Alentejo', + }, + { + country_short: 'BA', + region: 'Federacija Bosne i Hercegovine', + city: 'Zenica', + }, + { + country_short: 'HR', + region: 'Pozesko-slavonska zupanija', + city: 'Pozega', + }, + { + country_short: 'HR', + region: 'Sisacko-moslavacka zupanija', + city: 'Kutina', + }, + { + country_short: 'HR', + region: 'Varazdinska zupanija', + city: 'Varazdin', + }, + { + country_short: 'HR', + region: 'Licko-senjska zupanija', + city: 'Gospic', + }, + { + country_short: 'HR', + region: 'Osjecko-baranjska zupanija', + city: 'Valpovo', + }, + { + country_short: 'HR', + region: 'Zadarska zupanija', + city: 'Biograd na Moru', + }, + { + country_short: 'CZ', + region: 'Jihocesky kraj', + city: 'Ceske Budejovice', + }, + { + country_short: 'RU', + region: 'Komi, Respublika', + city: 'Komi, Respublika', + }, + { + country_short: 'AZ', + region: 'Saki', + city: 'Sheki', + }, + { + country_short: 'RU', + region: "Sakhalinskaya oblast'", + city: 'Korsakov', + }, + { + country_short: 'PY', + region: 'Asuncion', + city: 'Asuncion', + }, + { + country_short: 'CH', + region: 'Basel-Landschaft', + city: 'Pratteln', + }, + { + country_short: 'CH', + region: 'Aargau', + city: 'Baden', + }, + { + country_short: 'GE', + region: "K'akheti", + city: 'Sagarejo', + }, + { + country_short: 'AT', + region: 'Steiermark', + city: 'Grambach', + }, + { + country_short: 'EE', + region: 'Ida-Virumaa', + city: 'Johvi', + }, + { + country_short: 'JO', + region: 'Irbid', + city: 'Irbid', + }, + { + country_short: 'JO', + region: "Az Zarqa'", + city: 'Zarqa', + }, + { + country_short: 'JO', + region: "Ma'an", + city: 'Qir Moav', + }, + { + country_short: 'BY', + region: "Minskaya voblasts'", + city: 'Minsk', + }, + { + country_short: 'TR', + region: 'Nevsehir', + city: 'Nevsehir', + }, + { + country_short: 'TR', + region: 'Bartin', + city: 'Amasra', + }, + { + country_short: 'TR', + region: 'Osmaniye', + city: 'Toprakkale', + }, + { + country_short: 'TR', + region: 'Amasya', + city: 'Merzifon', + }, + { + country_short: 'TR', + region: 'Bitlis', + city: 'Tatvan', + }, + { + country_short: 'TR', + region: 'Batman', + city: 'Batman', + }, + { + country_short: 'TR', + region: 'Siirt', + city: 'Siirt', + }, + { + country_short: 'TR', + region: 'Karabuk', + city: 'Karabuk', + }, + { + country_short: 'CZ', + region: 'Pardubicky kraj', + city: 'Policka', + }, + { + country_short: 'RU', + region: 'Khanty-Mansiyskiy avtonomnyy okrug', + city: 'Surgut', + }, + { + country_short: 'UA', + region: 'Poltavska oblast', + city: 'Pyryatyn', + }, + { + country_short: 'UA', + region: 'Lvivska oblast', + city: "L'viv", + }, + { + country_short: 'UA', + region: 'Sumska oblast', + city: 'Romny', + }, + { + country_short: 'UA', + region: 'Odeska oblast', + city: 'Odessa', + }, + { + country_short: 'UA', + region: 'Luhanska oblast', + city: 'Svatove', + }, + { + country_short: 'BG', + region: 'Sofia', + city: 'Bozhurishte', + }, + { + country_short: 'BG', + region: 'Plovdiv', + city: 'Plovdiv', + }, + { + country_short: 'BG', + region: 'Montana', + city: 'Lom', + }, + { + country_short: 'BG', + region: 'Stara Zagora', + city: 'Chirpan', + }, + { + country_short: 'BG', + region: 'Varna', + city: 'Varna', + }, + { + country_short: 'BG', + region: 'Gabrovo', + city: 'Gabrovo', + }, + { + country_short: 'BG', + region: 'Pleven', + city: 'Pleven', + }, + { + country_short: 'BG', + region: 'Pazardzhik', + city: 'Pazardzhik', + }, + { + country_short: 'MD', + region: 'Balti', + city: 'Balti', + }, + { + country_short: 'MD', + region: 'Ocnita', + city: 'Otaci', + }, + { + country_short: 'MD', + region: 'Riscani', + city: 'Riscani', + }, + { + country_short: 'MD', + region: 'Gagauzia, Unitatea teritoriala autonoma', + city: 'Gagauzia, Unitatea teritoriala autonoma', + }, + { + country_short: 'MD', + region: 'Edinet', + city: 'Cupcini', + }, + { + country_short: 'MD', + region: 'Rezina', + city: 'Rezina', + }, + { + country_short: 'MD', + region: 'Glodeni', + city: 'Glodeni', + }, + { + country_short: 'MD', + region: 'Causeni', + city: 'Causeni', + }, + { + country_short: 'MD', + region: 'Ungheni', + city: 'Ungheni', + }, + { + country_short: 'MD', + region: 'Singerei', + city: 'Singerei', + }, + { + country_short: 'MD', + region: 'Telenesti', + city: 'Telenesti', + }, + { + country_short: 'MD', + region: 'Cimislia', + city: 'Cimislia', + }, + { + country_short: 'MD', + region: 'Orhei', + city: 'Orhei', + }, + { + country_short: 'MD', + region: 'Cahul', + city: 'Cahul', + }, + { + country_short: 'MD', + region: 'Nisporeni', + city: 'Nisporeni', + }, + { + country_short: 'LB', + region: 'Mont-Liban', + city: 'Jdaidet el Matn', + }, + { + country_short: 'KG', + region: 'Chuy', + city: 'Lebedinovka', + }, + { + country_short: 'KG', + region: 'Bishkek Shaary', + city: 'Bishkek', + }, + { + country_short: 'UA', + region: 'Chernivetska oblast', + city: 'Kelmentsi', + }, + { + country_short: 'PL', + region: 'Podkarpackie', + city: 'Debica', + }, + { + country_short: 'CH', + region: 'Valais', + city: 'Leuk', + }, + { + country_short: 'RU', + region: 'Dagestan, Respublika', + city: 'Dagestan, Respublika', + }, + { + country_short: 'CZ', + region: 'Kralovehradecky kraj', + city: 'Stare Nechanice', + }, + { + country_short: 'SK', + region: 'Presovsky kraj', + city: 'Presov', + }, + { + country_short: 'HR', + region: 'Viroviticko-podravska zupanija', + city: 'Virovitica', + }, + { + country_short: 'CZ', + region: 'Zlinsky kraj', + city: 'Vsetin', + }, + { + country_short: 'RU', + region: 'Karachayevo-Cherkesskaya Respublika', + city: 'Kardonikskaya', + }, + { + country_short: 'BA', + region: 'Republika Srpska', + city: 'Ugljevik', + }, + { + country_short: 'BA', + region: 'Brcko distrikt', + city: 'Cerik', + }, + { + country_short: 'SK', + region: 'Zilinsky kraj', + city: 'Liptovsky Mikulas', + }, + { + country_short: 'SK', + region: 'Kosicky kraj', + city: 'Kosice', + }, + { + country_short: 'NL', + region: 'Overijssel', + city: 'Hengelo', + }, + { + country_short: 'PL', + region: 'Warminsko-mazurskie', + city: 'Mragowo', + }, + { + country_short: 'PL', + region: 'Lubuskie', + city: 'Zagan', + }, + { + country_short: 'PL', + region: 'Podlaskie', + city: 'Rajgrod', + }, + { + country_short: 'PL', + region: 'Kujawsko-pomorskie', + city: 'Strzelno', + }, + { + country_short: 'PL', + region: 'Pomorskie', + city: 'Gdansk', + }, + { + country_short: 'FI', + region: 'Kymenlaakso', + city: 'Hamina', + }, + { + country_short: 'HU', + region: 'Hajdu-Bihar', + city: 'Hajduboszormeny', + }, + { + country_short: 'PH', + region: 'Benguet', + city: 'Baguio', + }, + { + country_short: 'VE', + region: 'Distrito Capital', + city: 'Caracas', + }, + { + country_short: 'CZ', + region: 'Jihomoravsky kraj', + city: 'Brno', + }, + { + country_short: 'US', + region: 'North Dakota', + city: 'Karlsruhe', + }, + { + country_short: 'IM', + region: 'Isle of Man', + city: 'Douglas', + }, + { + country_short: 'MT', + region: 'Valletta', + city: 'Valletta', + }, + { + country_short: 'GG', + region: 'Guernsey', + city: 'Saint Peter Port', + }, + { + country_short: 'MT', + region: "Ta' Xbiex", + city: "Ta' Xbiex", + }, + { + country_short: 'IE', + region: 'Tipperary', + city: 'Tipperary', + }, + { + country_short: 'GI', + region: 'Gibraltar', + city: 'Gibraltar', + }, + { + country_short: 'IQ', + region: 'Wasit', + city: 'Al Kut', + }, + { + country_short: 'IQ', + region: 'Babil', + city: 'Al Hillah', + }, + { + country_short: 'LY', + region: 'Tarabulus', + city: 'Tripoli', + }, + { + country_short: 'AM', + region: "Kotayk'", + city: 'Abovyan', + }, + { + country_short: 'AM', + region: 'Tavus', + city: 'Dilijan', + }, + { + country_short: 'AM', + region: 'Lori', + city: 'Gyulagarak', + }, + { + country_short: 'RU', + region: "Tomskaya oblast'", + city: 'Tomsk', + }, + { + country_short: 'RU', + region: 'Buryatiya, Respublika', + city: 'Buryatiya, Respublika', + }, + { + country_short: 'AM', + region: "Gegark'unik'", + city: 'Vaghashen', + }, + { + country_short: 'AM', + region: 'Sirak', + city: 'Gyumri', + }, + { + country_short: 'AM', + region: 'Armavir', + city: 'Merdzavan', + }, + { + country_short: 'AM', + region: 'Ararat', + city: 'Ararat', + }, + { + country_short: 'AM', + region: 'Vayoc Jor', + city: 'Armash', + }, + { + country_short: 'AM', + region: 'Aragacotn', + city: 'Byurakan', + }, + { + country_short: 'UA', + region: 'Zhytomyrska oblast', + city: "Korosten'", + }, + { + country_short: 'FI', + region: 'Varsinais-Suomi', + city: 'Turku', + }, + { + country_short: 'IE', + region: 'Roscommon', + city: 'Roscommon', + }, + { + country_short: 'IE', + region: 'Galway', + city: 'Ballinasloe', + }, + { + country_short: 'IE', + region: 'Longford', + city: 'Longford', + }, + { + country_short: 'YE', + region: "Amanat al 'Asimah", + city: 'Sanaa', + }, + { + country_short: 'YE', + region: 'Hadramawt', + city: 'Al Mukalla', + }, + { + country_short: 'YE', + region: "Ta'izz", + city: 'Mawiyah', + }, + { + country_short: 'BY', + region: 'Horad Minsk', + city: "Uruch'ye", + }, + { + country_short: 'BY', + region: "Hrodzyenskaya voblasts'", + city: 'Ashmyany', + }, + { + country_short: 'UZ', + region: 'Toshkent', + city: 'Tashkent', + }, + { + country_short: 'CH', + region: 'Sankt Gallen', + city: 'Sankt Gallen', + }, + { + country_short: 'YT', + region: 'Mamoudzou', + city: 'Mamoudzou', + }, + { + country_short: 'RE', + region: 'Reunion', + city: 'Reunion', + }, + { + country_short: 'GP', + region: 'Guadeloupe', + city: 'Baie-Mahault', + }, + { + country_short: 'MF', + region: 'Saint Martin (French Part)', + city: 'Marigot', + }, + { + country_short: 'MQ', + region: 'Martinique', + city: 'Le Lamentin', + }, + { + country_short: 'GF', + region: 'Guyane', + city: 'Cayenne', + }, + { + country_short: 'KW', + region: 'Hawalli', + city: 'Bayan', + }, + { + country_short: 'CH', + region: 'Luzern', + city: 'Meierskappel', + }, + { + country_short: 'UA', + region: 'Cherkaska oblast', + city: 'Kaniv', + }, + { + country_short: 'NZ', + region: 'Auckland', + city: 'Auckland', + }, + { + country_short: 'CH', + region: 'Ticino', + city: 'Agno', + }, + { + country_short: 'CH', + region: 'Fribourg', + city: 'Bulle', + }, + { + country_short: 'RS', + region: 'Macvanski okrug', + city: 'Vladimirci', + }, + { + country_short: 'RS', + region: 'Zlatiborski okrug', + city: 'Osjecenik', + }, + { + country_short: 'RS', + region: 'Podunavski okrug', + city: 'Smederevska Palanka', + }, + { + country_short: 'RS', + region: 'Prizrenski okrug', + city: 'Leposavic', + }, + { + country_short: 'RU', + region: "Zabaykal'skiy kray", + city: 'Gazimurskiy Zavod', + }, + { + country_short: 'RU', + region: 'Tyva, Respublika', + city: 'Tyva, Respublika', + }, + { + country_short: 'RU', + region: "Pskovskaya oblast'", + city: 'Pskov', + }, + { + country_short: 'RU', + region: 'Kabardino-Balkarskaya Respublika', + city: 'Nartkala', + }, + { + country_short: 'CH', + region: 'Solothurn', + city: 'Solothurn', + }, + { + country_short: 'BR', + region: 'Mato Grosso', + city: 'Feitoria do Tine', + }, + { + country_short: 'CO', + region: 'Cundinamarca', + city: 'San Antonio del Tequendama', + }, + { + country_short: 'PE', + region: 'Ucayali', + city: 'Aguaytia', + }, + { + country_short: 'MX', + region: 'Zacatecas', + city: 'Canitas de Felipe Pescador', + }, + { + country_short: 'PA', + region: 'Cocle', + city: 'El Cope', + }, + { + country_short: 'UY', + region: 'Durazno', + city: 'La Paloma', + }, + { + country_short: 'AR', + region: 'La Pampa', + city: 'Jacinto Arauz', + }, + { + country_short: 'CH', + region: 'Basel-Stadt', + city: 'Basel', + }, + { + country_short: 'HU', + region: 'Fejer', + city: 'Gardony', + }, + { + country_short: 'IE', + region: 'Wicklow', + city: 'Wicklow', + }, + { + country_short: 'GE', + region: 'Mtskheta-Mtianeti', + city: 'Zahesi', + }, + { + country_short: 'GE', + region: 'Ajaria', + city: 'Batumi', + }, + { + country_short: 'GE', + region: 'Imereti', + city: 'Kutaisi', + }, + { + country_short: 'GE', + region: 'Kvemo Kartli', + city: "Tsqnet'i", + }, + { + country_short: 'GE', + region: 'Shida Kartli', + city: 'Gori', + }, + { + country_short: 'GE', + region: 'Samegrelo-Zemo Svaneti', + city: "P'ot'i", + }, + { + country_short: 'GE', + region: 'Samtskhe-Javakheti', + city: 'Akhaltsikhe', + }, + { + country_short: 'CZ', + region: 'Plzensky kraj', + city: 'Susice', + }, + { + country_short: 'IE', + region: 'Louth', + city: 'Drogheda', + }, + { + country_short: 'SI', + region: 'Puconci', + city: 'Gorica', + }, + { + country_short: 'LK', + region: 'Western Province', + city: 'Colombo', + }, + { + country_short: 'PT', + region: 'Beja', + city: 'Sao Teotonio', + }, + { + country_short: 'AT', + region: 'Salzburg', + city: 'Bad Gastein', + }, + { + country_short: 'RU', + region: "Magadanskaya oblast'", + city: 'Magadan', + }, + { + country_short: 'PL', + region: 'Opolskie', + city: 'Kietrz', + }, + { + country_short: 'SZ', + region: 'Hhohho', + city: 'Mbabane', + }, + { + country_short: 'TR', + region: 'Bayburt', + city: 'Bayburt', + }, + { + country_short: 'TR', + region: 'Erzincan', + city: 'Erzincan', + }, + { + country_short: 'TR', + region: 'Mus', + city: 'Mus', + }, + { + country_short: 'TR', + region: 'Tokat', + city: 'Tokat', + }, + { + country_short: 'TR', + region: 'Sivas', + city: 'Zara', + }, + { + country_short: 'AZ', + region: 'Sumqayit', + city: 'Sumqayit', + }, + { + country_short: 'AZ', + region: 'Xacmaz', + city: 'Xacmaz', + }, + { + country_short: 'SK', + region: 'Trnavsky kraj', + city: 'Dunajska Streda', + }, + { + country_short: 'IE', + region: 'Meath', + city: 'Dunboyne', + }, + { + country_short: 'HU', + region: 'Baranya', + city: 'Pecs', + }, + { + country_short: 'UA', + region: 'Zaporizka oblast', + city: 'Rostushche', + }, + { + country_short: 'IQ', + region: "Karbala'", + city: 'Sulaymaniyah', + }, + { + country_short: 'KW', + region: "Al 'Asimah", + city: 'Kuwait', + }, + { + country_short: 'BD', + region: 'Dhaka', + city: 'Dhaka', + }, + { + country_short: 'BT', + region: 'Trongsa', + city: 'Trongsa', + }, + { + country_short: 'BN', + region: 'Brunei-Muara', + city: 'Peramu', + }, + { + country_short: 'HU', + region: 'Bekes', + city: 'Bekescsaba', + }, + { + country_short: 'HU', + region: 'Somogy', + city: 'Marcali', + }, + { + country_short: 'HU', + region: 'Komarom-Esztergom', + city: 'Kisber', + }, + { + country_short: 'HU', + region: 'Gyor-Moson-Sopron', + city: 'Nagycenk', + }, + { + country_short: 'HU', + region: 'Vas', + city: 'Buk', + }, + { + country_short: 'CY', + region: 'Keryneia', + city: 'Kyrenia', + }, + { + country_short: 'AZ', + region: 'Siyazan', + city: 'Alykhanly', + }, + { + country_short: 'AZ', + region: 'Masalli', + city: 'Boradigah', + }, + { + country_short: 'AZ', + region: 'Ganca', + city: 'Ganja', + }, + { + country_short: 'AZ', + region: 'Abseron', + city: 'Saray', + }, + { + country_short: 'AZ', + region: 'Mingacevir', + city: 'Mingelchaur', + }, + { + country_short: 'LT', + region: 'Siauliu apskritis', + city: 'Siauliai', + }, + { + country_short: 'HU', + region: 'Jasz-Nagykun-Szolnok', + city: 'Szolnok', + }, + { + country_short: 'HU', + region: 'Zala', + city: 'Zalakomar', + }, + { + country_short: 'HU', + region: 'Tolna', + city: 'Szekszard', + }, + { + country_short: 'HU', + region: 'Heves', + city: 'Belapatfalva', + }, + { + country_short: 'RS', + region: 'Kosovsko-Mitrovacki okrug', + city: 'Pristina', + }, + { + country_short: 'CH', + region: 'Thurgau', + city: 'Frauenfeld', + }, + { + country_short: 'CH', + region: 'Schwyz', + city: 'Wollerau', + }, + { + country_short: 'TR', + region: 'Kilis', + city: 'Kilis', + }, + { + country_short: 'UA', + region: 'Khersonska oblast', + city: 'Nova Kakhovka', + }, + { + country_short: 'UA', + region: 'Rivnenska oblast', + city: 'Rivne', + }, + { + country_short: 'PT', + region: 'Regiao Autonoma dos Acores', + city: 'Norte', + }, + { + country_short: 'PM', + region: 'Saint Pierre and Miquelon', + city: 'Miquelon', + }, + { + country_short: 'RS', + region: 'Pomoravski okrug', + city: 'Dubnica', + }, + { + country_short: 'RS', + region: 'Sumadijski okrug', + city: 'Petrovac', + }, + { + country_short: 'FI', + region: 'Etela-Pohjanmaa', + city: 'Seinajoki', + }, + { + country_short: 'IE', + region: 'Clare', + city: 'Shannon', + }, + { + country_short: 'LA', + region: 'Viangchan', + city: 'Vientiane', + }, + { + country_short: 'RO', + region: 'Botosani', + city: 'Paltinis', + }, + { + country_short: 'YE', + region: "'Adan", + city: "Al Ma'alla'", + }, + { + country_short: 'YE', + region: 'Hajjah', + city: 'Suq Shamar', + }, + { + country_short: 'YE', + region: "Sa'dah", + city: "Sa'dah", + }, + { + country_short: 'YE', + region: 'Al Hudaydah', + city: 'Al Hudaydah', + }, + { + country_short: 'YE', + region: "San'a'", + city: 'Sahar', + }, + { + country_short: 'YE', + region: "Ma'rib", + city: 'Suq Sirwah', + }, + { + country_short: 'YE', + region: "Al Bayda'", + city: "Rada'", + }, + { + country_short: 'AI', + region: 'Anguilla', + city: 'The Valley', + }, + { + country_short: 'GU', + region: 'Barrigada', + city: 'Barrigada Village', + }, + { + country_short: 'GU', + region: 'Dededo', + city: 'Dededo Municipality', + }, + { + country_short: 'MP', + region: 'Northern Mariana Islands', + city: 'Saipan', + }, + { + country_short: 'DO', + region: 'Distrito Nacional (Santo Domingo)', + city: 'Santo Domingo', + }, + { + country_short: 'DO', + region: 'La Vega', + city: 'Tireo Arriba', + }, + { + country_short: 'BT', + region: 'Thimphu', + city: 'Thimphu', + }, + { + country_short: 'CN', + region: 'Hubei', + city: 'Wuhan', + }, + { + country_short: 'NG', + region: 'Lagos', + city: 'Lagos', + }, + { + country_short: 'ZA', + region: 'Kwazulu-Natal', + city: 'Durban', + }, + { + country_short: 'CA', + region: 'Manitoba', + city: 'Winnipeg', + }, + { + country_short: 'PR', + region: 'Humacao', + city: 'Humacao', + }, + { + country_short: 'EC', + region: 'Pichincha', + city: 'Quito', + }, + { + country_short: 'BR', + region: 'Minas Gerais', + city: 'Belo Horizonte', + }, + { + country_short: 'EC', + region: 'Guayas', + city: 'Guayaquil', + }, + { + country_short: 'CN', + region: 'Sichuan', + city: 'Chengdu', + }, + { + country_short: 'BR', + region: 'Acre', + city: 'Minas Gerais', + }, + { + country_short: 'BR', + region: 'Tocantins', + city: 'Parana', + }, + { + country_short: 'CO', + region: 'Quindio', + city: 'Armenia', + }, + { + country_short: 'CO', + region: 'Atlantico', + city: 'Barranquilla', + }, + { + country_short: 'CO', + region: 'Risaralda', + city: 'Pereira', + }, + { + country_short: 'CO', + region: 'Antioquia', + city: 'Antioquia', + }, + { + country_short: 'CO', + region: 'Valle del Cauca', + city: 'Cali', + }, + { + country_short: 'CO', + region: 'Bolivar', + city: 'Cordoba', + }, + { + country_short: 'CO', + region: 'Meta', + city: 'Puerto Gaitan', + }, + { + country_short: 'CO', + region: 'Cesar', + city: 'Valledupar', + }, + { + country_short: 'CO', + region: 'Casanare', + city: 'Cauca', + }, + { + country_short: 'CO', + region: 'Santander', + city: 'Barbosa', + }, + { + country_short: 'CO', + region: 'Tolima', + city: 'Ibague', + }, + { + country_short: 'CO', + region: 'Caldas', + city: 'Manzanares', + }, + { + country_short: 'CO', + region: 'Narino', + city: 'Ipiales', + }, + { + country_short: 'EC', + region: 'Zamora Chinchipe', + city: 'Zamora', + }, + { + country_short: 'EC', + region: 'Cotopaxi', + city: 'Latacunga', + }, + { + country_short: 'EC', + region: 'Santo Domingo de los Tsachilas', + city: 'Francisco de Orellana', + }, + { + country_short: 'EC', + region: 'El Oro', + city: 'Portovelo', + }, + { + country_short: 'EC', + region: 'Los Rios', + city: 'Quevedo', + }, + { + country_short: 'EC', + region: 'Azuay', + city: 'Cuenca', + }, + { + country_short: 'EC', + region: 'Tungurahua', + city: 'Ambato', + }, + { + country_short: 'CO', + region: 'Huila', + city: 'Palermo', + }, + { + country_short: 'CO', + region: 'Norte de Santander', + city: 'Cucuta', + }, + { + country_short: 'CO', + region: 'Amazonas', + city: 'Tolima', + }, + { + country_short: 'CO', + region: 'Sucre', + city: 'Sucre', + }, + { + country_short: 'CO', + region: 'Cauca', + city: 'Popayan', + }, + { + country_short: 'CR', + region: 'San Jose', + city: 'San Jose', + }, + { + country_short: 'PE', + region: 'Arequipa', + city: 'Camana', + }, + { + country_short: 'MX', + region: 'Mexico', + city: 'Tlalnepantla', + }, + { + country_short: 'PR', + region: 'Ponce', + city: 'Ponce', + }, + { + country_short: 'PR', + region: 'Guaynabo', + city: 'Guaynabo', + }, + { + country_short: 'PR', + region: 'Salinas', + city: 'Coco', + }, + { + country_short: 'PR', + region: 'Cidra', + city: 'Bayamon', + }, + { + country_short: 'VI', + region: 'Virgin Islands, U.S.', + city: 'Virgin Islands, U.S.', + }, + { + country_short: 'PR', + region: 'San Juan', + city: 'San Juan', + }, + { + country_short: 'PR', + region: 'Carolina', + city: 'Carolina', + }, + { + country_short: 'PR', + region: 'Barceloneta', + city: 'Barceloneta', + }, + { + country_short: 'PR', + region: 'Rio Grande', + city: 'Rio Grande', + }, + { + country_short: 'PR', + region: 'Manati', + city: 'Manati', + }, + { + country_short: 'PR', + region: 'Dorado', + city: 'Dorado', + }, + { + country_short: 'PR', + region: 'Vega Alta', + city: 'Vega Alta', + }, + { + country_short: 'PR', + region: 'Arecibo', + city: 'Arecibo', + }, + { + country_short: 'SR', + region: 'Paramaribo', + city: 'Paramaribo', + }, + { + country_short: 'BO', + region: 'La Paz', + city: 'La Paz', + }, + { + country_short: 'PR', + region: 'Caguas', + city: 'Caguas', + }, + { + country_short: 'PR', + region: 'Aguadilla', + city: 'Aguadilla', + }, + { + country_short: 'PR', + region: 'Canovanas', + city: 'Canovanas', + }, + { + country_short: 'PR', + region: 'Mayaguez', + city: 'Mayaguez', + }, + { + country_short: 'PR', + region: 'Hatillo', + city: 'Hatillo', + }, + { + country_short: 'PR', + region: 'Cayey', + city: 'Cayey', + }, + { + country_short: 'PR', + region: 'Gurabo', + city: 'Gurabo', + }, + { + country_short: 'PR', + region: 'Morovis', + city: 'Morovis', + }, + { + country_short: 'PR', + region: 'Catano', + city: 'Catano', + }, + { + country_short: 'PR', + region: 'Guayama', + city: 'Guayama', + }, + { + country_short: 'PR', + region: 'Toa Alta', + city: 'Pajaros', + }, + { + country_short: 'BH', + region: 'Al Muharraq', + city: 'Al Muharraq', + }, + { + country_short: 'CN', + region: 'Guangxi Zhuangzu', + city: 'Nanning', + }, + { + country_short: 'PK', + region: 'Sindh', + city: 'Karachi', + }, + { + country_short: 'MN', + region: 'Ulaanbaatar', + city: 'Ulaanbaatar', + }, + { + country_short: 'JP', + region: 'Ishikawa', + city: 'Kanazawa', + }, + { + country_short: 'ID', + region: 'Jawa Barat', + city: 'Bandung', + }, + { + country_short: 'PH', + region: 'Surigao del Sur', + city: 'Cantilan', + }, + { + country_short: 'CN', + region: 'Chongqing', + city: 'Chongqing', + }, + { + country_short: 'NZ', + region: 'Wellington', + city: 'Wellington', + }, + { + country_short: 'NZ', + region: 'Otago', + city: 'Dunedin', + }, + { + country_short: 'NZ', + region: 'Bay of Plenty', + city: 'Tauranga', + }, + { + country_short: 'PG', + region: 'Chimbu', + city: 'Kundiawa', + }, + { + country_short: 'TL', + region: 'Dili', + city: 'Dili', + }, + { + country_short: 'SB', + region: 'Guadalcanal', + city: 'Honiara', + }, + { + country_short: 'PG', + region: 'Bougainville', + city: 'Panguna', + }, + { + country_short: 'SB', + region: 'Western', + city: 'Gizo', + }, + { + country_short: 'VU', + region: 'Shefa', + city: 'Port-Vila', + }, + { + country_short: 'FJ', + region: 'Central', + city: 'Suva', + }, + { + country_short: 'GU', + region: 'Piti', + city: 'Piti Village', + }, + { + country_short: 'CK', + region: 'Cook Islands', + city: 'Avarua', + }, + { + country_short: 'VU', + region: 'Torba', + city: 'Sola', + }, + { + country_short: 'PG', + region: 'Milne Bay', + city: 'Samarai', + }, + { + country_short: 'PG', + region: 'Central', + city: 'Idumava', + }, + { + country_short: 'PG', + region: 'West New Britain', + city: 'Kimbe', + }, + { + country_short: 'NZ', + region: 'Canterbury', + city: 'Christchurch', + }, + { + country_short: 'NZ', + region: 'West Coast', + city: 'Haast', + }, + { + country_short: 'TO', + region: 'Tongatapu', + city: "Nuku'alofa", + }, + { + country_short: 'FJ', + region: 'Rotuma', + city: 'Gaol', + }, + { + country_short: 'PG', + region: 'Manus', + city: 'Lorengau', + }, + { + country_short: 'NP', + region: 'Bagmati', + city: 'Kathmandu', + }, + { + country_short: 'KE', + region: 'Nairobi City', + city: 'Nairobi', + }, + { + country_short: 'IN', + region: 'Nagaland', + city: 'Kohima', + }, + { + country_short: 'IN', + region: 'Sikkim', + city: 'Gangtok', + }, + { + country_short: 'IN', + region: 'Arunachal Pradesh', + city: 'Naharlagun', + }, + { + country_short: 'VN', + region: 'Ha Nam', + city: 'Phu Ly', + }, + { + country_short: 'VN', + region: 'Dak Nong', + city: "B'tong", + }, + { + country_short: 'VN', + region: 'Bac Lieu', + city: 'Bac Lieu', + }, + { + country_short: 'VN', + region: 'Hau Giang', + city: 'Vi Thanh', + }, + { + country_short: 'VN', + region: 'Binh Phuoc', + city: 'Binh Long', + }, + { + country_short: 'VN', + region: 'Ha Tinh', + city: 'Ha Tinh', + }, + { + country_short: 'VN', + region: 'Son La', + city: 'Son La', + }, + { + country_short: 'VN', + region: 'Hoa Binh', + city: 'Hoa Binh', + }, + { + country_short: 'VN', + region: 'Quang Tri', + city: 'Dong Ha', + }, + { + country_short: 'VN', + region: 'Lai Chau', + city: 'Phong Tho', + }, + { + country_short: 'VN', + region: 'Bac Kan', + city: 'Bac Kan', + }, + { + country_short: 'VN', + region: 'Ha Giang', + city: 'Ha Giang', + }, + { + country_short: 'PG', + region: 'National Capital District (Port Moresby)', + city: 'Port Moresby', + }, + { + country_short: 'CN', + region: 'Hebei', + city: 'Shijiazhuang', + }, + { + country_short: 'CN', + region: 'Liaoning', + city: 'Dalian', + }, + { + country_short: 'CN', + region: 'Yunnan', + city: 'Tengyue', + }, + { + country_short: 'CN', + region: 'Hainan', + city: 'Dacheng', + }, + { + country_short: 'CN', + region: 'Hunan', + city: 'Huaqiao', + }, + { + country_short: 'CN', + region: 'Gansu', + city: 'Yulong', + }, + { + country_short: 'CN', + region: 'Anhui', + city: 'Chengjia', + }, + { + country_short: 'FI', + region: 'Pohjois-Savo', + city: 'Kuopio', + }, + { + country_short: 'CN', + region: 'Shandong', + city: 'Weifang', + }, + { + country_short: 'CN', + region: 'Tianjin', + city: 'Tianjin', + }, + { + country_short: 'MO', + region: 'Macao', + city: 'Macau', + }, + { + country_short: 'BR', + region: 'Pernambuco', + city: 'Parnamirim', + }, + { + country_short: 'NI', + region: 'Managua', + city: 'Managua', + }, + { + country_short: 'JM', + region: 'Kingston', + city: 'Kingston', + }, + { + country_short: 'AL', + region: 'Tirane', + city: 'Tirana', + }, + { + country_short: 'EG', + region: 'Al Qahirah', + city: 'Cairo', + }, + { + country_short: 'TT', + region: 'Port of Spain', + city: 'Port of Spain', + }, + { + country_short: 'MX', + region: 'Baja California', + city: 'Puebla', + }, + { + country_short: 'MX', + region: 'Sonora', + city: 'Hermosillo', + }, + { + country_short: 'MX', + region: 'Morelos', + city: 'Tlalnepantla', + }, + { + country_short: 'PH', + region: 'Cebu', + city: 'Cebu City', + }, + { + country_short: 'MX', + region: 'Jalisco', + city: 'Guadalajara', + }, + { + country_short: 'ID', + region: 'Jawa Timur', + city: 'Surabaya', + }, + { + country_short: 'MX', + region: 'Quintana Roo', + city: 'Cancun', + }, + { + country_short: 'ID', + region: 'Riau', + city: 'Pekanbaru', + }, + { + country_short: 'BR', + region: 'Goias', + city: 'Goiania', + }, + { + country_short: 'GT', + region: 'Guatemala', + city: 'Guatemala City', + }, + { + country_short: 'ID', + region: 'Banten', + city: 'Tangerang', + }, + { + country_short: 'CR', + region: 'Alajuela', + city: 'Alajuela', + }, + { + country_short: 'AF', + region: 'Kabul', + city: 'Kabul', + }, + { + country_short: 'AF', + region: 'Helmand', + city: 'Gereshk', + }, + { + country_short: 'AF', + region: 'Farah', + city: 'Farah', + }, + { + country_short: 'AF', + region: 'Herat', + city: 'Herat', + }, + { + country_short: 'AF', + region: 'Jowzjan', + city: 'Shibirghan', + }, + { + country_short: 'AF', + region: 'Badghis', + city: 'Ghormach', + }, + { + country_short: 'AF', + region: 'Samangan', + city: 'Aibak', + }, + { + country_short: 'AF', + region: 'Baghlan', + city: 'Pul-e Khumri', + }, + { + country_short: 'AF', + region: 'Takhar', + city: 'Taloqan', + }, + { + country_short: 'AF', + region: 'Kunduz', + city: 'Kunduz', + }, + { + country_short: 'BG', + region: 'Ruse', + city: 'Ruse', + }, + { + country_short: 'RU', + region: "Vladimirskaya oblast'", + city: 'Murom', + }, + { + country_short: 'MA', + region: 'Beni-Mellal-Khenifra', + city: 'Khenifra', + }, + { + country_short: 'MA', + region: 'Tanger-Tetouan-Al Hoceima', + city: 'Tangier', + }, + { + country_short: 'PR', + region: 'San Sebastian', + city: 'San Sebastian', + }, + { + country_short: 'PR', + region: 'Aguada', + city: 'Aguada', + }, + { + country_short: 'PR', + region: 'Villalba', + city: 'Villalba', + }, + { + country_short: 'PR', + region: 'Trujillo Alto', + city: 'Trujillo Alto', + }, + { + country_short: 'GD', + region: 'Saint George', + city: "Saint George's", + }, + { + country_short: 'VG', + region: 'Virgin Islands, British', + city: 'Virgin Islands, British', + }, + { + country_short: 'PR', + region: 'Cabo Rojo', + city: 'Cabo Rojo', + }, + { + country_short: 'KN', + region: 'Saint George Basseterre', + city: 'Basseterre', + }, + { + country_short: 'AG', + region: 'Saint John', + city: "Saint John's", + }, + { + country_short: 'PR', + region: 'Naranjito', + city: 'Naranjito', + }, + { + country_short: 'PR', + region: 'Barranquitas', + city: 'Barranquitas', + }, + { + country_short: 'BL', + region: 'Saint Barthelemy', + city: 'Gustavia', + }, + { + country_short: 'KN', + region: 'Saint John Figtree', + city: "Saint Peter's", + }, + { + country_short: 'PR', + region: 'Adjuntas', + city: 'Adjuntas', + }, + { + country_short: 'CA', + region: 'Newfoundland and Labrador', + city: "St. John's", + }, + { + country_short: 'PR', + region: 'Camuy', + city: 'Camuy', + }, + { + country_short: 'VC', + region: 'Saint George', + city: 'Kingstown', + }, + { + country_short: 'PR', + region: 'Corozal', + city: 'Corozal', + }, + { + country_short: 'BR', + region: 'Alagoas', + city: 'Maceio', + }, + { + country_short: 'BS', + region: 'New Providence', + city: 'Nassau', + }, + { + country_short: 'DM', + region: 'Saint George', + city: 'Roseau', + }, + { + country_short: 'KY', + region: 'Cayman Islands', + city: 'George Town', + }, + { + country_short: 'LC', + region: 'Castries', + city: 'Bisee', + }, + { + country_short: 'PR', + region: 'Fajardo', + city: 'Fajardo', + }, + { + country_short: 'ID', + region: 'Sulawesi Selatan', + city: 'Makassar', + }, + { + country_short: 'ID', + region: 'Kalimantan Selatan', + city: 'Banjarmasin', + }, + { + country_short: 'PH', + region: 'Laguna', + city: 'Calamba', + }, + { + country_short: 'MM', + region: 'Yangon', + city: 'Yangon', + }, + { + country_short: 'PH', + region: 'Quezon', + city: 'Lucena', + }, + { + country_short: 'MX', + region: 'Tamaulipas', + city: 'Reynosa', + }, + { + country_short: 'CL', + region: 'Arica y Parinacota', + city: 'Arica', + }, + { + country_short: 'CL', + region: 'Los Lagos', + city: 'Puerto Varas', + }, + { + country_short: 'BB', + region: 'Saint Michael', + city: 'Bridgetown', + }, + { + country_short: 'BB', + region: 'Saint John', + city: 'Saint Margarets', + }, + { + country_short: 'PR', + region: 'Bayamon', + city: 'Bayamon', + }, + { + country_short: 'PR', + region: 'Loiza', + city: 'Loiza', + }, + { + country_short: 'MX', + region: 'Campeche', + city: 'Campeche', + }, + { + country_short: 'PR', + region: 'Utuado', + city: 'Utuado', + }, + { + country_short: 'PR', + region: 'Vega Baja', + city: 'Vega Baja', + }, + { + country_short: 'PR', + region: 'Luquillo', + city: 'Luquillo', + }, + { + country_short: 'PR', + region: 'Yabucoa', + city: 'Yabucoa', + }, + { + country_short: 'PR', + region: 'Naguabo', + city: 'Naguabo', + }, + { + country_short: 'PR', + region: 'Hormigueros', + city: 'Hormigueros', + }, + { + country_short: 'PR', + region: 'Sabana Grande', + city: 'Sabana Grande', + }, + { + country_short: 'PR', + region: 'Comerio', + city: 'Comerio', + }, + { + country_short: 'PR', + region: 'Santa Isabel Municipio', + city: 'Santa Isabel', + }, + { + country_short: 'PR', + region: 'Aibonito', + city: 'Aibonito', + }, + { + country_short: 'PR', + region: 'Municipio de Juncos', + city: 'Juncos', + }, + { + country_short: 'PR', + region: 'Toa Baja', + city: 'Levittown', + }, + { + country_short: 'PR', + region: 'Lares', + city: 'Lares', + }, + { + country_short: 'PR', + region: 'Las Piedras', + city: 'La Fermina', + }, + { + country_short: 'PR', + region: 'Quebradillas', + city: 'Quebradillas', + }, + { + country_short: 'PR', + region: 'Municipio de Jayuya', + city: 'Jayuya', + }, + { + country_short: 'PR', + region: 'Penuelas', + city: 'Tallaboa Alta', + }, + { + country_short: 'PR', + region: 'San Lorenzo', + city: 'San Lorenzo', + }, + { + country_short: 'PR', + region: 'Moca', + city: 'Moca', + }, + { + country_short: 'PR', + region: 'Guanica', + city: 'Guanica', + }, + { + country_short: 'PR', + region: 'San German', + city: 'San German', + }, + { + country_short: 'PR', + region: 'Patillas', + city: 'Patillas', + }, + { + country_short: 'PR', + region: 'Arroyo', + city: 'Arroyo', + }, + { + country_short: 'PR', + region: 'Maunabo', + city: 'Maunabo', + }, + { + country_short: 'PR', + region: 'Coamo', + city: 'Coamo', + }, + { + country_short: 'PR', + region: 'Lajas', + city: 'Lajas', + }, + { + country_short: 'PR', + region: 'Ciales', + city: 'Ciales', + }, + { + country_short: 'PR', + region: 'Aguas Buenas', + city: 'Aguas Buenas', + }, + { + country_short: 'BS', + region: 'South Eleuthera', + city: 'Rock Sound', + }, + { + country_short: 'BS', + region: 'City of Freeport', + city: 'Lucaya', + }, + { + country_short: 'BS', + region: 'Central Abaco', + city: 'Marsh Harbour', + }, + { + country_short: 'BS', + region: 'West Grand Bahama', + city: 'West End', + }, + { + country_short: 'PR', + region: 'Juana Diaz', + city: 'Aguilita', + }, + { + country_short: 'PR', + region: 'Rincon', + city: 'Rincon', + }, + { + country_short: 'PR', + region: 'Anasco', + city: 'Anasco', + }, + { + country_short: 'PR', + region: 'Isabela', + city: 'Mora', + }, + { + country_short: 'LC', + region: 'Gros Islet', + city: 'Gros Islet', + }, + { + country_short: 'LC', + region: 'Laborie', + city: 'Laborie', + }, + { + country_short: 'LC', + region: 'Micoud', + city: 'Micoud', + }, + { + country_short: 'RS', + region: 'Borski okrug', + city: 'Bor', + }, + { + country_short: 'RS', + region: 'Jablanicki okrug', + city: 'Leskovac', + }, + { + country_short: 'RS', + region: 'Juznobacki okrug', + city: 'Novi Sad', + }, + { + country_short: 'RS', + region: 'Moravicki okrug', + city: 'Cacak', + }, + { + country_short: 'RS', + region: 'Sremski okrug', + city: 'Stara Pazova', + }, + { + country_short: 'RS', + region: 'Raski okrug', + city: 'Kraljevo', + }, + { + country_short: 'RS', + region: 'Pirotski okrug', + city: 'Pirot', + }, + { + country_short: 'RS', + region: 'Rasinski okrug', + city: 'Krusevac', + }, + { + country_short: 'RS', + region: 'Juznobanatski okrug', + city: 'Pancevo', + }, + { + country_short: 'PR', + region: 'Guayanilla', + city: 'Magas Arriba', + }, + { + country_short: 'PR', + region: 'Florida', + city: 'Florida', + }, + { + country_short: 'PR', + region: 'Las Marias', + city: 'Las Marias', + }, + { + country_short: 'PR', + region: 'Yauco', + city: 'Yauco', + }, + { + country_short: 'PR', + region: 'Vieques', + city: 'Esperanza', + }, + { + country_short: 'PR', + region: 'Ceiba', + city: 'Ceiba', + }, + { + country_short: 'BR', + region: 'Rondonia', + city: 'Jaru', + }, + { + country_short: 'BR', + region: 'Amazonas', + city: 'Manaus', + }, + { + country_short: 'PY', + region: 'Caaguazu', + city: 'Coronel Oviedo', + }, + { + country_short: 'DO', + region: 'Sanchez Ramirez', + city: 'Cevicos', + }, + { + country_short: 'DO', + region: 'Santiago', + city: 'Santiago de los Caballeros', + }, + { + country_short: 'GT', + region: 'Jalapa', + city: 'Jalapa', + }, + { + country_short: 'AR', + region: 'Buenos Aires', + city: 'Conesa', + }, + { + country_short: 'BR', + region: 'Espirito Santo', + city: 'Castelo', + }, + { + country_short: 'BR', + region: 'Santa Catarina', + city: 'Blumenau', + }, + { + country_short: 'BR', + region: 'Maranhao', + city: 'Sao Luis', + }, + { + country_short: 'UM', + region: 'Palmyra Atoll', + city: 'Palmyra Atoll', + }, + { + country_short: 'BS', + region: 'North Andros', + city: 'San Andros', + }, + { + country_short: 'AR', + region: 'Cordoba', + city: 'San Francisco', + }, + { + country_short: 'AR', + region: 'Entre Rios', + city: 'Parana', + }, + { + country_short: 'AR', + region: 'Santa Fe', + city: 'Rafaela', + }, + { + country_short: 'AR', + region: 'Salta', + city: 'Salta', + }, + { + country_short: 'AR', + region: 'Corrientes', + city: 'Corrientes', + }, + { + country_short: 'AR', + region: 'Neuquen', + city: 'Neuquen', + }, + { + country_short: 'BS', + region: 'Harbour Island', + city: 'Dunmore Town', + }, + { + country_short: 'BS', + region: 'North Eleuthera', + city: 'Alice Town', + }, + { + country_short: 'NP', + region: 'Seti', + city: 'Tikapur', + }, + { + country_short: 'NP', + region: 'Gandaki', + city: 'Pokhara', + }, + { + country_short: 'NP', + region: 'Kosi', + city: 'Ithari', + }, + { + country_short: 'NP', + region: 'Mechi', + city: 'Ilam', + }, + { + country_short: 'NP', + region: 'Lumbini', + city: 'Siddharthanagar', + }, + { + country_short: 'NP', + region: 'Rapti', + city: 'Tulsipur', + }, + { + country_short: 'NP', + region: 'Narayani', + city: 'Hetauda', + }, + { + country_short: 'NP', + region: 'Bheri', + city: 'Gulariya', + }, + { + country_short: 'NP', + region: 'Janakpur', + city: 'Janakpur', + }, + { + country_short: 'NP', + region: 'Sagarmatha', + city: 'Sukhipur', + }, + { + country_short: 'TM', + region: 'Ahal', + city: 'Ashgabat', + }, + { + country_short: 'ID', + region: 'Sumatera Utara', + city: 'Medan', + }, + { + country_short: 'ID', + region: 'Bali', + city: 'Denpasar', + }, + { + country_short: 'PK', + region: 'Azad Jammu and Kashmir', + city: 'New Mirpur', + }, + { + country_short: 'BD', + region: 'Chattogram', + city: 'Laksham', + }, + { + country_short: 'BD', + region: 'Sylhet', + city: 'Sylhet', + }, + { + country_short: 'TK', + region: 'Tokelau', + city: 'Fale old settlement', + }, + { + country_short: 'PH', + region: 'Bataan', + city: 'Roosevelt', + }, + { + country_short: 'PH', + region: 'Nueva Ecija', + city: 'San Francisco', + }, + { + country_short: 'PH', + region: 'Zambales', + city: 'San Juan', + }, + { + country_short: 'NZ', + region: 'Tasman', + city: 'Wakefield', + }, + { + country_short: 'PH', + region: 'Mindoro Occidental', + city: 'Summit', + }, + { + country_short: 'PH', + region: 'Davao del Sur', + city: 'Davao City', + }, + { + country_short: 'PH', + region: 'Zamboanga del Norte', + city: 'Seres', + }, + { + country_short: 'PH', + region: 'Rizal', + city: 'Sampaloc', + }, + { + country_short: 'PH', + region: 'Batangas', + city: 'Batangas', + }, + { + country_short: 'PH', + region: 'Lanao del Norte', + city: 'Maria Cristina', + }, + { + country_short: 'PH', + region: 'Negros Occidental', + city: 'Bacolod City', + }, + { + country_short: 'PH', + region: 'Aklan', + city: 'Kalibo', + }, + { + country_short: 'PH', + region: 'Bukidnon', + city: 'Quezon', + }, + { + country_short: 'PH', + region: 'Agusan del Norte', + city: 'Butuan', + }, + { + country_short: 'PH', + region: 'Cotabato', + city: 'New Cebu', + }, + { + country_short: 'PH', + region: 'Misamis Oriental', + city: 'Cagayan de Oro', + }, + { + country_short: 'PH', + region: 'Misamis Occidental', + city: 'Oroquieta', + }, + { + country_short: 'PH', + region: 'Bohol', + city: 'Tagbilaran', + }, + { + country_short: 'PH', + region: 'Sorsogon', + city: 'Bagacay', + }, + { + country_short: 'PH', + region: 'Capiz', + city: 'Roxas City', + }, + { + country_short: 'PH', + region: 'Zamboanga Sibugay', + city: 'Barangay Tetuan', + }, + { + country_short: 'PH', + region: 'Negros Oriental', + city: 'Dumaguete', + }, + { + country_short: 'PH', + region: 'Batanes', + city: 'Tacloban', + }, + { + country_short: 'PH', + region: 'Bulacan', + city: 'Plaridel', + }, + { + country_short: 'PH', + region: 'Isabela', + city: 'Echague', + }, + { + country_short: 'PH', + region: 'Cavite', + city: 'Patuto', + }, + { + country_short: 'PH', + region: 'Zamboanga del Sur', + city: 'Kagawasan', + }, + { + country_short: 'PH', + region: 'Camarines Norte', + city: 'Santa Elena', + }, + { + country_short: 'PH', + region: 'Antique', + city: 'Piape I', + }, + { + country_short: 'PH', + region: 'Mindoro Oriental', + city: 'Pili', + }, + { + country_short: 'MV', + region: 'Male', + city: 'Male', + }, + { + country_short: 'MV', + region: 'South Maalhosmadulu', + city: 'Eydhafushi', + }, + { + country_short: 'MV', + region: 'South Ari Atoll', + city: 'Mahibadhoo', + }, + { + country_short: 'MV', + region: 'Mulaku Atoll', + city: 'Muli', + }, + { + country_short: 'MV', + region: 'Felidhu Atoll', + city: 'Felidhoo', + }, + { + country_short: 'MV', + region: 'Hahdhunmathi', + city: 'Hithadhoo', + }, + { + country_short: 'MV', + region: 'Faadhippolhu', + city: 'Naifaru', + }, + { + country_short: 'MV', + region: 'Addu City', + city: 'Meedhoo', + }, + { + country_short: 'KH', + region: 'Banteay Mean Choay', + city: 'Paoy Pet', + }, + { + country_short: 'KH', + region: 'Pailin', + city: 'Pailin', + }, + { + country_short: 'ID', + region: 'Kepulauan Riau', + city: 'Riau', + }, + { + country_short: 'NC', + region: 'Province Sud', + city: 'Noumea', + }, + { + country_short: 'FJ', + region: 'Western', + city: 'Lautoka', + }, + { + country_short: 'WF', + region: 'Uvea', + city: 'Mata-Utu', + }, + { + country_short: 'BD', + region: 'Rajshahi', + city: 'Gaibandha', + }, + { + country_short: 'BD', + region: 'Rangpur', + city: 'Rangpur', + }, + { + country_short: 'BD', + region: 'Khulna', + city: 'Jessore', + }, + { + country_short: 'BD', + region: 'Barishal', + city: 'Barisal', + }, + { + country_short: 'NZ', + region: "Hawke's Bay", + city: 'Napier', + }, + { + country_short: 'NZ', + region: 'Gisborne', + city: 'Gisborne', + }, + { + country_short: 'NZ', + region: 'Northland', + city: 'Kerikeri', + }, + { + country_short: 'NZ', + region: 'Waikato', + city: 'Waiuku', + }, + { + country_short: 'NZ', + region: 'Manawatu-Wanganui', + city: 'Wanganui', + }, + { + country_short: 'PK', + region: 'Punjab', + city: 'Rawalpindi', + }, + { + country_short: 'MK', + region: 'Saraj', + city: 'Saraj', + }, + { + country_short: 'MK', + region: 'Bogovinje', + city: 'Dolno Palciste', + }, + { + country_short: 'MK', + region: 'Cucer Sandevo', + city: 'Shuto Orizari', + }, + { + country_short: 'RO', + region: 'Suceava', + city: 'Straja', + }, + { + country_short: 'RO', + region: 'Hunedoara', + city: 'Lupeni', + }, + { + country_short: 'RO', + region: 'Gorj', + city: 'Godinesti', + }, + { + country_short: 'CH', + region: 'Schaffhausen', + city: 'Schaffhausen', + }, + { + country_short: 'FI', + region: 'Keski-Pohjanmaa', + city: 'Kotka', + }, + { + country_short: 'IE', + region: 'Mayo', + city: 'Belmullet', + }, + { + country_short: 'AT', + region: 'Burgenland', + city: 'Eisenstadt', + }, + { + country_short: 'MK', + region: 'Kratovo', + city: 'Kratovo', + }, + { + country_short: 'MK', + region: 'Kumanovo', + city: 'Kumanovo', + }, + { + country_short: 'CZ', + region: 'Olomoucky kraj', + city: 'Prostejov', + }, + { + country_short: 'RU', + region: 'Kamchatskiy kray', + city: 'Tilichiki', + }, + { + country_short: 'RU', + region: 'Chukotskiy avtonomnyy okrug', + city: 'Provideniya', + }, + { + country_short: 'RU', + region: "Amurskaya oblast'", + city: 'Tokur', + }, + { + country_short: 'LI', + region: 'Eschen', + city: 'Eschen', + }, + { + country_short: 'CH', + region: 'Neuchatel', + city: 'Neuchatel', + }, + { + country_short: 'CH', + region: 'Appenzell Ausserrhoden', + city: 'Herisau', + }, + { + country_short: 'QA', + region: 'Ar Rayyan', + city: 'Ar Rayyan', + }, + { + country_short: 'QA', + region: 'Umm Salal', + city: 'Umm Salal Muhammad', + }, + { + country_short: 'MK', + region: 'Strumica', + city: 'Strumica', + }, + { + country_short: 'MK', + region: 'Brvenica', + city: 'Miletino', + }, + { + country_short: 'MK', + region: 'Dolneni', + city: 'Desovo', + }, + { + country_short: 'MK', + region: 'Tetovo', + city: 'Tetovo', + }, + { + country_short: 'MK', + region: 'Kisela Voda', + city: 'Usje', + }, + { + country_short: 'MK', + region: 'Veles', + city: 'Veles', + }, + { + country_short: 'MK', + region: 'Ohrid', + city: 'Ohrid', + }, + { + country_short: 'KW', + region: 'Al Ahmadi', + city: 'Al Ahmadi', + }, + { + country_short: 'MU', + region: 'Port Louis', + city: 'Port Louis', + }, + { + country_short: 'BG', + region: 'Haskovo', + city: 'Dimitrovgrad', + }, + { + country_short: 'BG', + region: 'Targovishte', + city: 'Popovo', + }, + { + country_short: 'RO', + region: 'Brasov', + city: 'Brasov', + }, + { + country_short: 'RS', + region: 'Zajecarski okrug', + city: 'Zajecar', + }, + { + country_short: 'SI', + region: 'Zrece', + city: 'Zrece', + }, + { + country_short: 'SI', + region: 'Pivka', + city: 'Pivka', + }, + { + country_short: 'SI', + region: 'Maribor', + city: 'Maribor', + }, + { + country_short: 'SI', + region: 'Radovljica', + city: 'Radovljica', + }, + { + country_short: 'SI', + region: 'Bled', + city: 'Bled', + }, + { + country_short: 'SI', + region: 'Dobrova-Polhov Gradec', + city: 'Podsmreka', + }, + { + country_short: 'SI', + region: 'Beltinci', + city: 'Lipovci', + }, + { + country_short: 'SI', + region: 'Trzin', + city: 'Trzin', + }, + { + country_short: 'SI', + region: 'Tolmin', + city: 'Tolmin', + }, + { + country_short: 'SI', + region: 'Kungota', + city: 'Slatina', + }, + { + country_short: 'SI', + region: 'Bohinj', + city: 'Bohinjska Bistrica', + }, + { + country_short: 'SI', + region: 'Skofja Loka', + city: 'Skofja Loka', + }, + { + country_short: 'SI', + region: 'Naklo', + city: 'Spodnja Besnica', + }, + { + country_short: 'SI', + region: 'Koper', + city: 'Koper', + }, + { + country_short: 'SI', + region: 'Jesenice', + city: 'Koroska Bela', + }, + { + country_short: 'SI', + region: 'Luce', + city: 'Erjavec', + }, + { + country_short: 'SI', + region: 'Gorje', + city: 'Poljsica pri Gorjah', + }, + { + country_short: 'SI', + region: 'Postojna', + city: 'Postojna', + }, + { + country_short: 'SI', + region: 'Murska Sobota', + city: 'Murska Sobota', + }, + { + country_short: 'SI', + region: 'Trbovlje', + city: 'Trbovlje', + }, + { + country_short: 'SI', + region: 'Logatec', + city: 'Logatec', + }, + { + country_short: 'SI', + region: 'Kocevje', + city: 'Kocevje', + }, + { + country_short: 'SI', + region: 'Lendava', + city: 'Lendava', + }, + { + country_short: 'SI', + region: 'Dobrovnik', + city: 'Strehovci', + }, + { + country_short: 'SI', + region: 'Zalec', + city: 'Zalec', + }, + { + country_short: 'SI', + region: 'Majsperk', + city: 'Kupcinji Vrh', + }, + { + country_short: 'SI', + region: 'Kamnik', + city: 'Smarca', + }, + { + country_short: 'SI', + region: 'Vodice', + city: 'Vodice', + }, + { + country_short: 'SI', + region: 'Mirna Pec', + city: 'Orkljevec', + }, + { + country_short: 'SI', + region: 'Celje', + city: 'Celje', + }, + { + country_short: 'SI', + region: 'Gornji Petrovci', + city: 'Gornji Petrovci', + }, + { + country_short: 'SI', + region: 'Grosuplje', + city: 'Grosuplje', + }, + { + country_short: 'SI', + region: 'Kidricevo', + city: 'Kidricevo', + }, + { + country_short: 'SI', + region: 'Kranjska Gora', + city: 'Kranjska Gora', + }, + { + country_short: 'SI', + region: 'Sostanj', + city: 'Ravne', + }, + { + country_short: 'SI', + region: 'Piran', + city: 'Lucija', + }, + { + country_short: 'SI', + region: 'Prebold', + city: 'Prebold', + }, + { + country_short: 'SI', + region: 'Skofljica', + city: 'Skofljica', + }, + { + country_short: 'AL', + region: 'Elbasan', + city: 'Elbasan', + }, + { + country_short: 'CY', + region: 'Lemesos', + city: 'Limassol', + }, + { + country_short: 'CY', + region: 'Larnaka', + city: 'Aradippou', + }, + { + country_short: 'BY', + region: "Vitsyebskaya voblasts'", + city: 'Baranovichi', + }, + { + country_short: 'BY', + region: "Brestskaya voblasts'", + city: 'Brest', + }, + { + country_short: 'KG', + region: 'Ysyk-Kol', + city: 'Karakol', + }, + { + country_short: 'KG', + region: 'Osh Shaary', + city: 'Osh', + }, + { + country_short: 'KG', + region: 'Naryn', + city: 'Naryn', + }, + { + country_short: 'KG', + region: 'Jalal-Abad', + city: 'Tash-Kumyr', + }, + { + country_short: 'CZ', + region: 'Liberecky kraj', + city: 'Ceska Lipa', + }, + { + country_short: 'MD', + region: 'Stinga Nistrului, unitatea teritoriala din', + city: 'Stinga Nistrului, unitatea teritoriala din', + }, + { + country_short: 'MD', + region: 'Bender', + city: 'Bender', + }, + { + country_short: 'MD', + region: 'Criuleni', + city: 'Tiraspolul Nou', + }, + { + country_short: 'YE', + region: 'Ibb', + city: 'Dhi as Sufal', + }, + { + country_short: 'UZ', + region: 'Samarqand', + city: 'Samarqand', + }, + { + country_short: 'UZ', + region: 'Qashqadaryo', + city: 'Qarshi', + }, + { + country_short: 'UZ', + region: 'Buxoro', + city: 'Buxoro', + }, + { + country_short: 'UA', + region: 'Sevastopol', + city: 'Sevastopol', + }, + { + country_short: 'SM', + region: 'Citta di San Marino', + city: 'San Marino', + }, + { + country_short: 'BG', + region: 'Smolyan', + city: 'Chepelare', + }, + { + country_short: 'RU', + region: 'Mordoviya, Respublika', + city: 'Mordoviya, Respublika', + }, + { + country_short: 'HR', + region: 'Medimurska zupanija', + city: 'Kotoriba', + }, + { + country_short: 'HR', + region: 'Sibensko-kninska zupanija', + city: 'Rogoznica', + }, + { + country_short: 'HR', + region: 'Dubrovacko-neretvanska zupanija', + city: 'Opuzen', + }, + { + country_short: 'AM', + region: "Syunik'", + city: 'Goris', + }, + { + country_short: 'SK', + region: 'Banskobystricky kraj', + city: 'Krupina', + }, + { + country_short: 'RU', + region: 'Chechenskaya Respublika', + city: 'Pervomayskaya', + }, + { + country_short: 'TR', + region: 'Ardahan', + city: 'Ardahan', + }, + { + country_short: 'TR', + region: 'Kirklareli', + city: 'Luleburgaz', + }, + { + country_short: 'CY', + region: 'Ammochostos', + city: 'Paralimni', + }, + { + country_short: 'CY', + region: 'Pafos', + city: 'Peyia', + }, + { + country_short: 'CH', + region: 'Glarus', + city: 'Netstal', + }, + { + country_short: 'CH', + region: 'Obwalden', + city: 'Engelberg', + }, + { + country_short: 'IE', + region: 'Donegal', + city: 'Ballyshannon', + }, + { + country_short: 'IE', + region: 'Cavan', + city: 'Bailieborough', + }, + { + country_short: 'LV', + region: 'Kuldigas novads', + city: 'Kuldiga', + }, + { + country_short: 'LV', + region: 'Preilu novads', + city: 'Preili', + }, + { + country_short: 'AZ', + region: 'Qusar', + city: 'Samur', + }, + { + country_short: 'AZ', + region: 'Qazax', + city: 'Qazax', + }, + { + country_short: 'AZ', + region: 'Quba', + city: 'Quba', + }, + { + country_short: 'LU', + region: 'Wiltz', + city: 'Wiltz', + }, + { + country_short: 'RU', + region: "Yevreyskaya avtonomnaya oblast'", + city: 'Khingansk', + }, + { + country_short: 'RU', + region: 'Kalmykiya, Respublika', + city: 'Kalmykiya, Respublika', + }, + { + country_short: 'IE', + region: 'Leitrim', + city: 'Drumshanbo', + }, + { + country_short: 'IE', + region: 'Carlow', + city: 'Carlow', + }, + { + country_short: 'IE', + region: 'Offaly', + city: 'Edenderry', + }, + { + country_short: 'IE', + region: 'Wexford', + city: 'Ferns', + }, + { + country_short: 'KW', + region: 'Al Farwaniyah', + city: 'Ar Rabiyah', + }, + { + country_short: 'KW', + region: "Al Jahra'", + city: "Al Jahra'", + }, + { + country_short: 'KW', + region: 'Mubarak al Kabir', + city: 'Sabah as Salim', + }, + { + country_short: 'ME', + region: 'Podgorica', + city: 'Podgorica', + }, + { + country_short: 'ME', + region: 'Berane', + city: 'Berane', + }, + { + country_short: 'ME', + region: 'Ulcinj', + city: 'Ulcinj', + }, + { + country_short: 'ME', + region: 'Bar', + city: 'Sutomore', + }, + { + country_short: 'ME', + region: 'Herceg-Novi', + city: 'Herceg-Novi', + }, + { + country_short: 'LT', + region: 'Panevezio apskritis', + city: 'Panevezys', + }, + { + country_short: 'CD', + region: 'Tshopo', + city: 'Kisangani', + }, + { + country_short: 'IS', + region: 'Nordurland eystra', + city: 'Akureyri', + }, + { + country_short: 'IS', + region: 'Nordurland vestra', + city: 'Saudarkrokur', + }, + { + country_short: 'IE', + region: 'Kilkenny', + city: 'Kilkenny', + }, + { + country_short: 'CH', + region: 'Jura', + city: 'Alle', + }, + { + country_short: 'IQ', + region: 'Diyala', + city: 'Baqubah', + }, + { + country_short: 'SV', + region: 'San Salvador', + city: 'Aguilares', + }, + { + country_short: 'SV', + region: 'Morazan', + city: 'San Francisco', + }, + { + country_short: 'SV', + region: 'Chalatenango', + city: 'Chalatenango', + }, + { + country_short: 'SV', + region: 'Cuscatlan', + city: 'Cojutepeque', + }, + { + country_short: 'SV', + region: 'Usulutan', + city: 'Usulutan', + }, + { + country_short: 'SV', + region: 'Santa Ana', + city: 'Santa Ana', + }, + { + country_short: 'PA', + region: 'Chiriqui', + city: 'Boqueron', + }, + { + country_short: 'SV', + region: 'Cabanas', + city: 'Sensuntepeque', + }, + { + country_short: 'SV', + region: 'Ahuachapan', + city: 'Ahuachapan', + }, + { + country_short: 'SV', + region: 'San Miguel', + city: 'Moncagua', + }, + { + country_short: 'SV', + region: 'La Union', + city: 'La Union', + }, + { + country_short: 'SV', + region: 'La Libertad', + city: 'Lourdes', + }, + { + country_short: 'CL', + region: 'Valparaiso', + city: 'Valparaiso', + }, + { + country_short: 'CL', + region: 'Biobio', + city: 'Concepcion', + }, + { + country_short: 'MX', + region: 'Chihuahua', + city: 'Ciudad Juarez', + }, + { + country_short: 'MX', + region: 'Durango', + city: 'Juarez', + }, + { + country_short: 'MX', + region: 'Guanajuato', + city: 'Silao', + }, + { + country_short: 'MX', + region: 'Coahuila de Zaragoza', + city: 'Torreon', + }, + { + country_short: 'MX', + region: 'Puebla', + city: 'Huejotzingo', + }, + { + country_short: 'AR', + region: 'Mendoza', + city: 'San Martin', + }, + { + country_short: 'AR', + region: 'San Luis', + city: 'San Luis', + }, + { + country_short: 'BR', + region: 'Para', + city: 'Campo', + }, + { + country_short: 'CL', + region: 'Antofagasta', + city: 'Antofagasta', + }, + { + country_short: 'CL', + region: 'Tarapaca', + city: 'Iquique', + }, + { + country_short: 'MX', + region: 'San Luis Potosi', + city: 'San Luis Potosi', + }, + { + country_short: 'MX', + region: 'Tlaxcala', + city: 'Tlaxcala', + }, + { + country_short: 'MX', + region: 'Hidalgo', + city: 'Pachuca de Soto', + }, + { + country_short: 'PA', + region: 'Herrera', + city: 'Pese', + }, + { + country_short: 'PA', + region: 'Ngobe-Bugle', + city: 'Santos', + }, + { + country_short: 'PA', + region: 'Veraguas', + city: 'San Francisco', + }, + { + country_short: 'PA', + region: 'Colon', + city: 'Colon', + }, + { + country_short: 'SV', + region: 'La Paz', + city: 'San Luis Talpa', + }, + { + country_short: 'CR', + region: 'Heredia', + city: 'Heredia', + }, + { + country_short: 'CR', + region: 'Cartago', + city: 'Cartago', + }, + { + country_short: 'DO', + region: 'Peravia', + city: 'Matanzas', + }, + { + country_short: 'MX', + region: 'Veracruz de Ignacio de la Llave', + city: 'Nogales', + }, + { + country_short: 'FI', + region: 'Etela-Karjala', + city: 'Lappeenranta', + }, + { + country_short: 'AD', + region: 'Andorra la Vella', + city: 'Andorra la Vella', + }, + { + country_short: 'MC', + region: 'Monaco-Ville', + city: 'Monaco', + }, + { + country_short: 'BO', + region: 'Santa Cruz', + city: 'Santa Cruz de la Sierra', + }, + { + country_short: 'ID', + region: 'Kalimantan Timur', + city: 'Balikpapan', + }, + { + country_short: 'ID', + region: 'Kalimantan Barat', + city: 'Pontianak', + }, + { + country_short: 'ID', + region: 'Sulawesi Tengah', + city: 'Tolitoli', + }, + { + country_short: 'ID', + region: 'Jawa Tengah', + city: 'Boyolali', + }, + { + country_short: 'KH', + region: 'Siem Reab', + city: 'Siem Reap', + }, + { + country_short: 'KH', + region: 'Stueng Traeng', + city: 'Stung Treng', + }, + { + country_short: 'KH', + region: 'Kampong Chaam', + city: 'Kampong Cham', + }, + { + country_short: 'KH', + region: 'Prey Veaeng', + city: 'Prey Veng', + }, + { + country_short: 'KH', + region: 'Preah Sihanouk', + city: 'Sihanoukville', + }, + { + country_short: 'KH', + region: 'Baat Dambang', + city: 'Battambang', + }, + { + country_short: 'KH', + region: 'Taakaev', + city: 'Phumi Veal Sre', + }, + { + country_short: 'KH', + region: 'Kaeb', + city: 'Krong Kep', + }, + { + country_short: 'KH', + region: 'Kampong Thum', + city: 'Kampong Thom', + }, + { + country_short: 'KH', + region: 'Kampot', + city: 'Kampot', + }, + { + country_short: 'KH', + region: 'Kracheh', + city: 'Kratie', + }, + { + country_short: 'KH', + region: 'Preah Vihear', + city: 'Tbeng Meanchey', + }, + { + country_short: 'KH', + region: 'Kandaal', + city: 'Ta Khmau', + }, + { + country_short: 'KH', + region: 'Pousaat', + city: 'Pursat', + }, + { + country_short: 'KH', + region: 'Mondol Kiri', + city: 'Senmonourom', + }, + { + country_short: 'KH', + region: 'Svaay Rieng', + city: 'Bavet', + }, + { + country_short: 'CN', + region: 'Jilin', + city: 'Changchun', + }, + { + country_short: 'ID', + region: 'Kepulauan Bangka Belitung', + city: 'Pangkalpinang', + }, + { + country_short: 'ID', + region: 'Lampung', + city: 'Bandarlampung', + }, + { + country_short: 'ID', + region: 'Sumatera Selatan', + city: 'Palembang', + }, + { + country_short: 'ID', + region: 'Jambi', + city: 'Jambi', + }, + { + country_short: 'ID', + region: 'Yogyakarta', + city: 'Yogyakarta', + }, + { + country_short: 'ID', + region: 'Nusa Tenggara Barat', + city: 'Mataram', + }, + { + country_short: 'ID', + region: 'Sumatera Barat', + city: 'Padang', + }, + { + country_short: 'ID', + region: 'Kalimantan Utara', + city: 'Tarakan', + }, + { + country_short: 'ID', + region: 'Kalimantan Tengah', + city: 'Palangkaraya', + }, + { + country_short: 'ID', + region: 'Papua', + city: 'Nabire', + }, + { + country_short: 'ID', + region: 'Maluku', + city: 'Passo', + }, + { + country_short: 'ID', + region: 'Gorontalo', + city: 'Isimu', + }, + { + country_short: 'ID', + region: 'Aceh', + city: 'Lhokseumawe', + }, + { + country_short: 'ID', + region: 'Maluku Utara', + city: 'Ternate', + }, + { + country_short: 'ID', + region: 'Papua Barat', + city: 'Manokwari', + }, + { + country_short: 'ID', + region: 'Sulawesi Tenggara', + city: 'Unaaha', + }, + { + country_short: 'ID', + region: 'Nusa Tenggara Timur', + city: 'Ende', + }, + { + country_short: 'ID', + region: 'Bengkulu', + city: 'Bengkulu', + }, + { + country_short: 'ID', + region: 'Sulawesi Barat', + city: 'Mamasa', + }, + { + country_short: 'CN', + region: 'Xinjiang Uygur', + city: 'Altay', + }, + { + country_short: 'CN', + region: 'Qinghai', + city: 'Xining', + }, + { + country_short: 'PK', + region: 'Khyber Pakhtunkhwa', + city: 'Peshawar', + }, + { + country_short: 'PH', + region: 'Davao de Oro', + city: 'San-Miguel', + }, + { + country_short: 'PH', + region: 'Davao del Norte', + city: 'Cabili', + }, + { + country_short: 'ME', + region: 'Kotor', + city: 'Kotor', + }, + { + country_short: 'FI', + region: 'Satakunta', + city: 'Rauma', + }, + { + country_short: 'BY', + region: "Homyel'skaya voblasts'", + city: "Homyel'", + }, + { + country_short: 'JO', + region: "Al Balqa'", + city: 'As Salt', + }, + { + country_short: 'GE', + region: 'Abkhazia', + city: 'Sokhumi', + }, + { + country_short: 'GL', + region: 'Kommuneqarfik Sermersooq', + city: 'Nuuk', + }, + { + country_short: 'GL', + region: 'Qeqqata Kommunia', + city: 'Maniitsoq', + }, + { + country_short: 'AZ', + region: 'Salyan', + city: 'Salyan', + }, + { + country_short: 'SI', + region: 'Divaca', + city: 'Divaca', + }, + { + country_short: 'SI', + region: 'Izola', + city: 'Izola', + }, + { + country_short: 'BE', + region: 'Liege', + city: 'Liege', + }, + { + country_short: 'RS', + region: 'Pcinjski okrug', + city: 'Vranje', + }, + { + country_short: 'RS', + region: 'Toplicki okrug', + city: 'Sokolovica', + }, + { + country_short: 'NG', + region: 'Abuja Federal Capital Territory', + city: 'Abuja', + }, + { + country_short: 'AL', + region: 'Vlore', + city: 'Vlore', + }, + { + country_short: 'AL', + region: 'Durres', + city: 'Durres', + }, + { + country_short: 'AL', + region: 'Lezhe', + city: 'Kurbnesh', + }, + { + country_short: 'MK', + region: 'Kavadarci', + city: 'Kavadarci', + }, + { + country_short: 'MD', + region: 'Anenii Noi', + city: 'Varnita', + }, + { + country_short: 'RU', + region: 'Kareliya, Respublika', + city: 'Kareliya, Respublika', + }, + { + country_short: 'PT', + region: 'Regiao Autonoma da Madeira', + city: 'Funchal', + }, + { + country_short: 'AZ', + region: 'Naxcivan', + city: 'Demirchi', + }, + { + country_short: 'FI', + region: 'Etela-Savo', + city: 'Mikkeli', + }, + { + country_short: 'FI', + region: 'Paijat-Hame', + city: 'Sysma', + }, + { + country_short: 'FI', + region: 'Pohjois-Pohjanmaa', + city: 'Oulu', + }, + { + country_short: 'FI', + region: 'Lappi', + city: 'Kemi', + }, + { + country_short: 'FI', + region: 'Kainuu', + city: 'Kajaani', + }, + { + country_short: 'FI', + region: 'Pohjois-Karjala', + city: 'Tuupovaara', + }, + { + country_short: 'MD', + region: 'Ialoveni', + city: 'Cigirleni', + }, + { + country_short: 'RS', + region: 'Severnobacki okrug', + city: 'Subotica', + }, + { + country_short: 'IE', + region: 'Waterford', + city: 'Passage East', + }, + { + country_short: 'BZ', + region: 'Cayo', + city: 'Belmopan', + }, + { + country_short: 'SK', + region: 'Trenciansky kraj', + city: 'Nove Mesto nad Vahom', + }, + { + country_short: 'BG', + region: 'Burgas', + city: 'Burgas', + }, + { + country_short: 'AT', + region: 'Tirol', + city: 'Telfs', + }, + { + country_short: 'BG', + region: 'Pernik', + city: 'Pernik', + }, + { + country_short: 'MD', + region: 'Floresti', + city: 'Floresti', + }, + { + country_short: 'MT', + region: 'Sliema', + city: 'Sliema', + }, + { + country_short: 'MT', + region: 'Saint Lawrence', + city: 'Dwejra', + }, + { + country_short: 'MT', + region: 'Bormla', + city: 'Cospicua', + }, + { + country_short: 'MT', + region: 'Pieta', + city: 'Gwardamanga', + }, + { + country_short: 'MT', + region: 'Attard', + city: 'Attard', + }, + { + country_short: 'MT', + region: 'Dingli', + city: 'Dingli', + }, + { + country_short: 'MT', + region: 'Mosta', + city: 'Mosta', + }, + { + country_short: 'MT', + region: 'Qormi', + city: 'Qormi', + }, + { + country_short: 'MT', + region: 'Saint John', + city: 'Saint John', + }, + { + country_short: 'MT', + region: 'Mdina', + city: 'Imdina', + }, + { + country_short: 'MT', + region: 'Zabbar', + city: 'Zabbar', + }, + { + country_short: 'MT', + region: 'Birzebbuga', + city: 'Birzebbuga', + }, + { + country_short: 'MT', + region: 'Lija', + city: 'Lija', + }, + { + country_short: 'MT', + region: 'Zebbug Gozo', + city: 'Zebbug', + }, + { + country_short: 'MT', + region: 'Msida', + city: 'Msida', + }, + { + country_short: 'MT', + region: "Saint Paul's Bay", + city: 'San Pawl il-Bahar', + }, + { + country_short: 'MT', + region: 'Mgarr', + city: 'Ghajn Tuffieha', + }, + { + country_short: 'MT', + region: 'Swieqi', + city: 'Swieqi', + }, + { + country_short: 'MD', + region: 'Hincesti', + city: 'Horjesti', + }, + { + country_short: 'MD', + region: 'Straseni', + city: 'Straseni', + }, + { + country_short: 'MD', + region: 'Briceni', + city: 'Briceni', + }, + { + country_short: 'MD', + region: 'Cantemir', + city: 'Visniovca', + }, + { + country_short: 'MD', + region: 'Taraclia', + city: 'Valea Perjei', + }, + { + country_short: 'IQ', + region: 'Al Basrah', + city: 'Basrah', + }, + { + country_short: 'TJ', + region: 'Dushanbe', + city: 'Dushanbe', + }, + { + country_short: 'TJ', + region: 'Sughd', + city: 'Chkalovsk', + }, + { + country_short: 'CH', + region: 'Appenzell Innerrhoden', + city: 'Appenzell', + }, + { + country_short: 'UZ', + region: "Farg'ona", + city: 'Toshloq', + }, + { + country_short: 'MM', + region: 'Kayah', + city: 'Loikaw', + }, + { + country_short: 'MM', + region: 'Mandalay', + city: 'Mandalay', + }, + { + country_short: 'MM', + region: 'Shan', + city: 'Myothit', + }, + { + country_short: 'MM', + region: 'Sagaing', + city: 'Shwebo', + }, + { + country_short: 'MM', + region: 'Ayeyarwady', + city: 'Kyaiklat', + }, + { + country_short: 'MM', + region: 'Bago', + city: 'Nyaunglebin', + }, + { + country_short: 'MM', + region: 'Kachin', + city: 'Myitkyina', + }, + { + country_short: 'PK', + region: 'Islamabad', + city: 'Islamabad', + }, + { + country_short: 'PK', + region: 'Balochistan', + city: 'Gwadar', + }, + { + country_short: 'FO', + region: 'Streymoy', + city: 'Torshavn', + }, + { + country_short: 'ME', + region: 'Bijelo Polje', + city: 'Bijelo Polje', + }, + { + country_short: 'ME', + region: 'Tivat', + city: 'Tivat', + }, + { + country_short: 'ME', + region: 'Budva', + city: 'Budva', + }, + { + country_short: 'ME', + region: 'Rozaje', + city: 'Rozaje', + }, + { + country_short: 'ME', + region: 'Plav', + city: 'Skic', + }, + { + country_short: 'ME', + region: 'Kolasin', + city: 'Kolasin', + }, + { + country_short: 'JO', + region: "Al 'Aqabah", + city: 'Aqaba', + }, + { + country_short: 'BG', + region: 'Dobrich', + city: 'Dobrich', + }, + { + country_short: 'BH', + region: 'Al Janubiyah', + city: 'Jaww', + }, + { + country_short: 'BH', + region: 'Ash Shamaliyah', + city: "Madinat 'Isa", + }, + { + country_short: 'EE', + region: 'Tartumaa', + city: 'Laeva', + }, + { + country_short: 'EE', + region: 'Viljandimaa', + city: 'Mustla', + }, + { + country_short: 'EE', + region: 'Parnumaa', + city: 'Parnu', + }, + { + country_short: 'EE', + region: 'Laanemaa', + city: 'Haapsalu', + }, + { + country_short: 'EE', + region: 'Jogevamaa', + city: 'Poltsamaa', + }, + { + country_short: 'BG', + region: 'Vratsa', + city: 'Kozloduy', + }, + { + country_short: 'BG', + region: 'Yambol', + city: 'Yambol', + }, + { + country_short: 'QA', + region: 'Al Wakrah', + city: 'Al Wukayr', + }, + { + country_short: 'QA', + region: 'Ash Shamal', + city: "Ar Ru'ays", + }, + { + country_short: 'JO', + region: 'Al Karak', + city: "'Izra", + }, + { + country_short: 'JO', + region: 'Jarash', + city: "Ar Rujm al 'Ali", + }, + { + country_short: 'JO', + region: 'Al Mafraq', + city: 'Mafraq', + }, + { + country_short: 'JO', + region: "'Ajlun", + city: 'Mintaqat al Hawuz', + }, + { + country_short: 'JO', + region: 'Madaba', + city: 'Madaba', + }, + { + country_short: 'QA', + region: 'Al Khawr wa adh Dhakhirah', + city: 'Al Khawr', + }, + { + country_short: 'QA', + region: "Az Za'ayin", + city: "Az Za'ayin", + }, + { + country_short: 'KG', + region: 'Talas', + city: 'Kyzyl-Adyr', + }, + { + country_short: 'SC', + region: 'Beau Vallon', + city: 'Beau Vallon', + }, + { + country_short: 'SI', + region: 'Kranj', + city: 'Kranj', + }, + { + country_short: 'GL', + region: 'Avannaata Kommunia', + city: 'Ilulissat', + }, + { + country_short: 'IQ', + region: 'Salah ad Din', + city: 'Ad Dujayl', + }, + { + country_short: 'IQ', + region: 'Al Qadisiyah', + city: "'Afak", + }, + { + country_short: 'IQ', + region: 'Al Muthanna', + city: 'As Samawah', + }, + { + country_short: 'IQ', + region: 'Dhi Qar', + city: 'Ash Shatrah', + }, + { + country_short: 'IQ', + region: 'Al Anbar', + city: 'Hit', + }, + { + country_short: 'IQ', + region: 'Maysan', + city: 'Al Majar al Kabir', + }, + { + country_short: 'IQ', + region: 'Dahuk', + city: 'Dahuk', + }, + { + country_short: 'AL', + region: 'Diber', + city: 'Peshkopi', + }, + { + country_short: 'VE', + region: 'Zulia', + city: 'Maracaibo', + }, + { + country_short: 'AR', + region: 'Chaco', + city: 'San Bernardo', + }, + { + country_short: 'MX', + region: 'Michoacan de Ocampo', + city: 'Tancitaro', + }, + { + country_short: 'DO', + region: 'Maria Trinidad Sanchez', + city: 'Nagua', + }, + { + country_short: 'DO', + region: 'Espaillat', + city: 'Rio San Juan', + }, + { + country_short: 'DO', + region: 'Valverde', + city: 'Esperanza', + }, + { + country_short: 'DO', + region: 'Puerto Plata', + city: 'Sosua', + }, + { + country_short: 'BR', + region: 'Mato Grosso do Sul', + city: 'Jardim', + }, + { + country_short: 'VE', + region: 'Falcon', + city: 'Punto Fijo', + }, + { + country_short: 'VE', + region: 'Aragua', + city: 'Maracay', + }, + { + country_short: 'MX', + region: 'Guerrero', + city: 'Cuautepec', + }, + { + country_short: 'BR', + region: 'Amapa', + city: 'Macapa', + }, + { + country_short: 'AR', + region: 'San Juan', + city: 'Chimbas', + }, + { + country_short: 'AR', + region: 'Chubut', + city: 'Comodoro Rivadavia', + }, + { + country_short: 'DO', + region: 'Monte Plata', + city: 'Esperalvillo', + }, + { + country_short: 'VE', + region: 'La Guaira', + city: 'Maiquetia', + }, + { + country_short: 'VE', + region: 'Lara', + city: 'Guayana', + }, + { + country_short: 'VE', + region: 'Bolivar', + city: 'Ciudad Guayana', + }, + { + country_short: 'VE', + region: 'Anzoategui', + city: 'El Tigre', + }, + { + country_short: 'DO', + region: 'San Pedro de Macoris', + city: 'Los Llanos', + }, + { + country_short: 'DO', + region: 'Samana', + city: 'Samana', + }, + { + country_short: 'AR', + region: 'Santiago del Estero', + city: 'Santiago del Estero', + }, + { + country_short: 'VE', + region: 'Carabobo', + city: 'Valencia', + }, + { + country_short: 'MX', + region: 'Aguascalientes', + city: 'Calvillo', + }, + { + country_short: 'VE', + region: 'Nueva Esparta', + city: 'Porlamar', + }, + { + country_short: 'AR', + region: 'La Rioja', + city: 'La Rioja', + }, + { + country_short: 'DO', + region: 'Monsenor Nouel', + city: 'Bonao', + }, + { + country_short: 'BR', + region: 'Paraiba', + city: 'Santa Luzia', + }, + { + country_short: 'MX', + region: 'Chiapas', + city: 'Reforma', + }, + { + country_short: 'MX', + region: 'Tabasco', + city: 'Villahermosa', + }, + { + country_short: 'DO', + region: 'San Cristobal', + city: 'Villa Altagracia', + }, + { + country_short: 'ZA', + region: 'Mpumalanga', + city: 'White River', + }, + { + country_short: 'ZA', + region: 'Free State', + city: 'Sasolburg', + }, + { + country_short: 'ZA', + region: 'Northern Cape', + city: 'Kathu', + }, + { + country_short: 'ZA', + region: 'Eastern Cape', + city: 'Port Elizabeth', + }, + { + country_short: 'ZA', + region: 'Limpopo', + city: 'Polokwane', + }, + { + country_short: 'ZA', + region: 'North-West', + city: 'Photsaneng', + }, + { + country_short: 'EG', + region: 'Al Jizah', + city: 'Giza', + }, + { + country_short: 'EG', + region: 'Ash Sharqiyah', + city: 'Bilbays', + }, + { + country_short: 'EG', + region: 'Al Qalyubiyah', + city: 'Al Khankah', + }, + { + country_short: 'EG', + region: "Janub Sina'", + city: 'Sharm el-Sheikh', + }, + { + country_short: 'EG', + region: "Bur Sa'id", + city: 'Port Said', + }, + { + country_short: 'EG', + region: 'Al Gharbiyah', + city: 'Al Mahallah al Kubra', + }, + { + country_short: 'EG', + region: 'Al Buhayrah', + city: 'Damanhur', + }, + { + country_short: 'EG', + region: 'Dumyat', + city: 'Damietta', + }, + { + country_short: 'EG', + region: 'Al Bahr al Ahmar', + city: 'Hurghada', + }, + { + country_short: 'EG', + region: 'Matruh', + city: "Al 'Alamayn", + }, + { + country_short: 'EG', + region: 'As Suways', + city: 'Ain Sukhna', + }, + { + country_short: 'EG', + region: 'Aswan', + city: 'Aswan', + }, + { + country_short: 'EG', + region: 'Asyut', + city: 'Manfalut', + }, + { + country_short: 'EG', + region: 'Al Minufiyah', + city: 'Shibin al Kawm', + }, + { + country_short: 'EG', + region: "Shamal Sina'", + city: 'Arish', + }, + { + country_short: 'EG', + region: 'Bani Suwayf', + city: 'Bani Suwayf', + }, + { + country_short: 'EG', + region: 'Ad Daqahliyah', + city: 'Al Mansurah', + }, + { + country_short: 'EG', + region: 'Al Fayyum', + city: 'Ibshaway', + }, + { + country_short: 'EG', + region: 'Al Uqsur', + city: 'Luxor', + }, + { + country_short: 'EG', + region: 'Kafr ash Shaykh', + city: 'Fuwwah', + }, + { + country_short: 'EG', + region: "Al Isma'iliyah", + city: 'At Tall al Kabir', + }, + { + country_short: 'EG', + region: 'Qina', + city: 'Farshut', + }, + { + country_short: 'EG', + region: 'Suhaj', + city: 'Jirja', + }, + { + country_short: 'LR', + region: 'Montserrado', + city: 'Monrovia', + }, + { + country_short: 'KE', + region: 'Kajiado', + city: 'Ngong', + }, + { + country_short: 'KE', + region: 'Nakuru', + city: 'Nakuru', + }, + { + country_short: 'KE', + region: 'Kericho', + city: 'Litein', + }, + { + country_short: 'NG', + region: 'Imo', + city: 'Owerri', + }, + { + country_short: 'NG', + region: 'Niger', + city: 'Minna', + }, + { + country_short: 'NG', + region: 'Ondo', + city: 'Ondo', + }, + { + country_short: 'NG', + region: 'Enugu', + city: 'Enugu', + }, + { + country_short: 'NG', + region: 'Zamfara', + city: 'Gusau', + }, + { + country_short: 'NG', + region: 'Sokoto', + city: 'Sokoto', + }, + { + country_short: 'NG', + region: 'Kwara', + city: 'Ilorin', + }, + { + country_short: 'NG', + region: 'Kaduna', + city: 'Kaduna', + }, + { + country_short: 'NG', + region: 'Osun', + city: 'Ogun', + }, + { + country_short: 'NG', + region: 'Borno', + city: 'Maiduguri', + }, + { + country_short: 'NG', + region: 'Rivers', + city: 'Omoku', + }, + { + country_short: 'NG', + region: 'Delta', + city: 'Agbor', + }, + { + country_short: 'NG', + region: 'Kano', + city: 'Kano', + }, + { + country_short: 'NG', + region: 'Katsina', + city: 'Katsina', + }, + { + country_short: 'NG', + region: 'Kogi', + city: 'Ankpa', + }, + { + country_short: 'NG', + region: 'Nasarawa', + city: 'Akwanga', + }, + { + country_short: 'NG', + region: 'Cross River', + city: 'Calabar', + }, + { + country_short: 'NG', + region: 'Anambra', + city: 'Nnewi', + }, + { + country_short: 'NG', + region: 'Plateau', + city: 'Pankshin', + }, + { + country_short: 'NG', + region: 'Gombe', + city: 'Gombe', + }, + { + country_short: 'NG', + region: 'Yobe', + city: 'Potiskum', + }, + { + country_short: 'NG', + region: 'Adamawa', + city: 'Yola', + }, + { + country_short: 'NG', + region: 'Abia', + city: 'Arochukwu', + }, + { + country_short: 'NG', + region: 'Edo', + city: 'Ekpoma', + }, + { + country_short: 'NG', + region: 'Jigawa', + city: 'Gumel', + }, + { + country_short: 'NG', + region: 'Oyo', + city: 'Ibadan', + }, + { + country_short: 'ZM', + region: 'Lusaka', + city: 'Lusaka', + }, + { + country_short: 'ZM', + region: 'Southern', + city: 'Livingstone', + }, + { + country_short: 'ZM', + region: 'Luapula', + city: 'Kawambwa', + }, + { + country_short: 'ZM', + region: 'Central', + city: 'Kabwe', + }, + { + country_short: 'ZW', + region: 'Matabeleland South', + city: 'Beitbridge', + }, + { + country_short: 'ZW', + region: 'Mashonaland East', + city: 'Mount Hampden', + }, + { + country_short: 'ZW', + region: 'Matabeleland North', + city: 'Victoria Falls', + }, + { + country_short: 'ZW', + region: 'Bulawayo', + city: 'Bulawayo', + }, + { + country_short: 'ZM', + region: 'Copperbelt', + city: 'Kitwe', + }, + { + country_short: 'BW', + region: 'South East', + city: 'Gaborone', + }, + { + country_short: 'ZM', + region: 'Muchinga', + city: 'Mpika', + }, + { + country_short: 'MZ', + region: 'Nampula', + city: 'Mozambique', + }, + { + country_short: 'KE', + region: 'Kiambu', + city: 'Kiambu', + }, + { + country_short: 'KE', + region: 'Homa Bay', + city: 'Oyugis', + }, + { + country_short: 'TN', + region: 'Bizerte', + city: 'Mateur', + }, + { + country_short: 'TN', + region: 'Mahdia', + city: 'El Jem', + }, + { + country_short: 'TN', + region: 'Sfax', + city: 'Sfax', + }, + { + country_short: 'TN', + region: 'Tunis', + city: 'Tunis', + }, + { + country_short: 'TN', + region: 'Gafsa', + city: 'Ar Rudayyif', + }, + { + country_short: 'TN', + region: 'Sousse', + city: 'Sousse', + }, + { + country_short: 'TN', + region: 'Beja', + city: 'Bardo', + }, + { + country_short: 'TN', + region: 'Nabeul', + city: 'Manzil Bu Zalafah', + }, + { + country_short: 'TN', + region: 'Medenine', + city: 'Zarzis', + }, + { + country_short: 'TN', + region: 'Kairouan', + city: 'Kairouan', + }, + { + country_short: 'TN', + region: 'Monastir', + city: 'Monastir', + }, + { + country_short: 'TN', + region: "L'Ariana", + city: 'Ariana', + }, + { + country_short: 'TN', + region: 'La Manouba', + city: 'Manouba', + }, + { + country_short: 'TN', + region: 'Gabes', + city: 'Gabes', + }, + { + country_short: 'TN', + region: 'Jendouba', + city: 'Wadi Maliz', + }, + { + country_short: 'TN', + region: 'Ben Arous', + city: 'Ben Arous', + }, + { + country_short: 'TN', + region: 'Tozeur', + city: 'Nefta', + }, + { + country_short: 'TN', + region: 'Kebili', + city: 'Bechri', + }, + { + country_short: 'TN', + region: 'Sidi Bouzid', + city: 'Sidi Bouzid', + }, + { + country_short: 'TN', + region: 'Zaghouan', + city: 'El Fahs', + }, + { + country_short: 'TN', + region: 'Tataouine', + city: 'Tataouine', + }, + { + country_short: 'TN', + region: 'Siliana', + city: 'Siliana', + }, + { + country_short: 'ZM', + region: 'Western', + city: 'Mukuba', + }, + { + country_short: 'MG', + region: 'Antananarivo', + city: 'Antananarivo', + }, + { + country_short: 'MG', + region: 'Toliara', + city: 'Belo Tsiribihina', + }, + { + country_short: 'MG', + region: 'Mahajanga', + city: 'Mahajanga', + }, + { + country_short: 'AO', + region: 'Luanda', + city: 'Luanda', + }, + { + country_short: 'AO', + region: 'Malange', + city: 'Malanje', + }, + { + country_short: 'AO', + region: 'Bie', + city: 'Kuito', + }, + { + country_short: 'AO', + region: 'Benguela', + city: 'Benguela', + }, + { + country_short: 'NA', + region: 'Khomas', + city: 'Windhoek', + }, + { + country_short: 'NA', + region: 'Oshikoto', + city: 'Tsumeb', + }, + { + country_short: 'NA', + region: 'Erongo', + city: 'Walvis Bay', + }, + { + country_short: 'NA', + region: 'Kunene', + city: 'Khorixas', + }, + { + country_short: 'NA', + region: 'Hardap', + city: 'Aranos', + }, + { + country_short: 'EG', + region: 'Al Minya', + city: 'Bani Mazar', + }, + { + country_short: 'CI', + region: 'Abidjan', + city: 'Abidjan', + }, + { + country_short: 'CI', + region: 'Comoe', + city: 'Aboisso', + }, + { + country_short: 'CI', + region: 'Yamoussoukro', + city: 'Nzuessi', + }, + { + country_short: 'CI', + region: 'Lagunes', + city: 'Adzope', + }, + { + country_short: 'CI', + region: 'Savanes', + city: 'Korhogo', + }, + { + country_short: 'GH', + region: 'Ashanti', + city: 'Kumasi', + }, + { + country_short: 'GH', + region: 'Eastern', + city: 'Koforidua', + }, + { + country_short: 'GH', + region: 'Upper East', + city: 'Navrongo', + }, + { + country_short: 'GH', + region: 'Upper West', + city: 'Jirapa', + }, + { + country_short: 'GH', + region: 'Western', + city: 'Takoradi', + }, + { + country_short: 'GH', + region: 'Northern', + city: 'Tamale', + }, + { + country_short: 'GH', + region: 'Bono', + city: 'Sunyani', + }, + { + country_short: 'SD', + region: 'Khartoum', + city: 'Khartoum', + }, + { + country_short: 'SD', + region: 'Kassala', + city: 'Kassala', + }, + { + country_short: 'SD', + region: 'Blue Nile', + city: 'Ad-Damazin', + }, + { + country_short: 'SD', + region: 'Gezira', + city: 'Wad Medani', + }, + { + country_short: 'SD', + region: 'South Darfur', + city: 'Nyala', + }, + { + country_short: 'SD', + region: 'Northern', + city: 'Dongola', + }, + { + country_short: 'CI', + region: 'Vallee du Bandama', + city: 'Katiola', + }, + { + country_short: 'CI', + region: 'Bas-Sassandra', + city: 'San-Pedro', + }, + { + country_short: 'CI', + region: 'Woroba', + city: 'Seguela', + }, + { + country_short: 'CI', + region: 'Lacs', + city: 'Dimbokro', + }, + { + country_short: 'CI', + region: 'Sassandra-Marahoue', + city: 'Vavoua', + }, + { + country_short: 'CI', + region: 'Montagnes', + city: 'Danane', + }, + { + country_short: 'NG', + region: 'Ogun', + city: 'Agbara', + }, + { + country_short: 'NG', + region: 'Akwa Ibom', + city: 'Eket', + }, + { + country_short: 'MW', + region: 'Zomba', + city: 'Zomba', + }, + { + country_short: 'MW', + region: 'Lilongwe', + city: 'Lilongwe', + }, + { + country_short: 'MW', + region: 'Blantyre', + city: 'Blantyre', + }, + { + country_short: 'MW', + region: 'Machinga', + city: 'Makata', + }, + { + country_short: 'MW', + region: 'Mzimba', + city: 'Mzuzu', + }, + { + country_short: 'AO', + region: 'Cabinda', + city: 'Cabinda', + }, + { + country_short: 'AO', + region: 'Huambo', + city: 'Huambo', + }, + { + country_short: 'AO', + region: 'Huila', + city: 'Lubango', + }, + { + country_short: 'AO', + region: 'Cuanza-Sul', + city: 'Sumbe', + }, + { + country_short: 'ZM', + region: 'Eastern', + city: 'Petauke', + }, + { + country_short: 'ZM', + region: 'Northern', + city: 'Kasama', + }, + { + country_short: 'KE', + region: 'Kilifi', + city: 'Malindi', + }, + { + country_short: 'KE', + region: 'Kwale', + city: 'Kwale', + }, + { + country_short: 'KE', + region: 'Taita/Taveta', + city: 'Wundanyi', + }, + { + country_short: 'KE', + region: 'Nyandarua', + city: 'Ndaragwa', + }, + { + country_short: 'KE', + region: 'Siaya', + city: 'Siaya', + }, + { + country_short: 'KE', + region: 'Machakos', + city: 'Athi River', + }, + { + country_short: 'KE', + region: 'Uasin Gishu', + city: 'Eldoret', + }, + { + country_short: 'KE', + region: 'Kisumu', + city: 'Kisumu', + }, + { + country_short: 'KE', + region: 'Kisii', + city: 'Kisii', + }, + { + country_short: 'KE', + region: 'Elgeyo/Marakwet', + city: 'Githurai', + }, + { + country_short: 'KE', + region: 'Kirinyaga', + city: 'Kerugoya', + }, + { + country_short: 'KE', + region: 'Embu', + city: 'Embu', + }, + { + country_short: 'KE', + region: 'Meru', + city: 'Nkubu', + }, + { + country_short: 'KE', + region: 'Kitui', + city: 'Kitui', + }, + { + country_short: 'KE', + region: 'Laikipia', + city: 'Nanyuki', + }, + { + country_short: 'ML', + region: 'Koulikoro', + city: 'Koulikoro', + }, + { + country_short: 'ML', + region: 'Bamako', + city: 'Bamako', + }, + { + country_short: 'ML', + region: 'Sikasso', + city: 'Bougouni', + }, + { + country_short: 'ML', + region: 'Segou', + city: 'Segou', + }, + { + country_short: 'BJ', + region: 'Oueme', + city: 'Porto-Novo', + }, + { + country_short: 'BJ', + region: 'Littoral', + city: 'Cotonou', + }, + { + country_short: 'BJ', + region: 'Atlantique', + city: 'Ouidah', + }, + { + country_short: 'BW', + region: 'Kweneng', + city: 'Mogoditshane', + }, + { + country_short: 'BW', + region: 'North East', + city: 'Francistown', + }, + { + country_short: 'BW', + region: 'Central', + city: 'Palapye', + }, + { + country_short: 'BW', + region: 'Kgatleng', + city: 'Mochudi', + }, + { + country_short: 'LY', + region: 'Banghazi', + city: 'Benghazi', + }, + { + country_short: 'GH', + region: 'Central', + city: 'Winneba', + }, + { + country_short: 'CI', + region: 'Goh-Djiboua', + city: 'Divo', + }, + { + country_short: 'CV', + region: 'Praia', + city: 'Praia', + }, + { + country_short: 'CV', + region: 'Sao Vicente', + city: 'Mindelo', + }, + { + country_short: 'CV', + region: 'Brava', + city: 'Nova Sintra', + }, + { + country_short: 'CV', + region: 'Sal', + city: 'Santa Maria', + }, + { + country_short: 'CV', + region: 'Ribeira Grande de Santiago', + city: 'Cidade Velha', + }, + { + country_short: 'RW', + region: 'Ville de Kigali', + city: 'Kigali', + }, + { + country_short: 'RW', + region: 'Est', + city: 'Kibungo', + }, + { + country_short: 'RW', + region: 'Sud', + city: 'Nyanza', + }, + { + country_short: 'AO', + region: 'Lunda-Sul', + city: 'Saurimo', + }, + { + country_short: 'CG', + region: 'Brazzaville', + city: 'Brazzaville', + }, + { + country_short: 'CG', + region: 'Pointe-Noire', + city: 'Pointe-Noire', + }, + { + country_short: 'NG', + region: 'Bayelsa', + city: 'Ghana', + }, + { + country_short: 'MW', + region: 'Karonga', + city: 'Livingstonia', + }, + { + country_short: 'UG', + region: 'Wakiso', + city: 'Kireka', + }, + { + country_short: 'UG', + region: 'Jinja', + city: 'Jinja', + }, + { + country_short: 'MZ', + region: 'Tete', + city: 'Tete', + }, + { + country_short: 'MZ', + region: 'Maputo', + city: 'Maputo', + }, + { + country_short: 'GM', + region: 'Western', + city: 'Sukuta', + }, + { + country_short: 'GM', + region: 'Banjul', + city: 'Banjul', + }, + { + country_short: 'GM', + region: 'North Bank', + city: 'Farafenni', + }, + { + country_short: 'GM', + region: 'Central River', + city: 'Bansang', + }, + { + country_short: 'GM', + region: 'Lower River', + city: 'Soma', + }, + { + country_short: 'LS', + region: 'Maseru', + city: 'Maseru', + }, + { + country_short: 'MU', + region: 'Plaines Wilhems', + city: 'Quatre Bornes', + }, + { + country_short: 'MU', + region: 'Moka', + city: 'Moka', + }, + { + country_short: 'MZ', + region: 'Sofala', + city: 'Beira', + }, + { + country_short: 'KE', + region: 'Makueni', + city: 'Wote', + }, + { + country_short: 'MG', + region: 'Antsiranana', + city: 'Antsiranana', + }, + { + country_short: 'SS', + region: 'Central Equatoria', + city: 'Juba', + }, + { + country_short: 'BW', + region: 'Southern', + city: 'Jwaneng', + }, + { + country_short: 'MA', + region: 'Marrakech-Safi', + city: 'Marrakesh', + }, + { + country_short: 'NE', + region: 'Niamey', + city: 'Niamey', + }, + { + country_short: 'GN', + region: 'Conakry', + city: 'Conakry', + }, + { + country_short: 'GN', + region: 'Dubreka', + city: 'Dubreka', + }, + { + country_short: 'GN', + region: 'Boke', + city: 'Kamsar', + }, + { + country_short: 'CD', + region: 'Haut-Katanga', + city: 'Lubumbashi', + }, + { + country_short: 'CD', + region: 'Lualaba', + city: 'Goma', + }, + { + country_short: 'BF', + region: 'Kadiogo', + city: 'Ouagadougou', + }, + { + country_short: 'SO', + region: 'Banaadir', + city: 'Mogadishu', + }, + { + country_short: 'SO', + region: 'Shabeellaha Hoose', + city: 'Afgooye', + }, + { + country_short: 'ZW', + region: 'Mashonaland West', + city: 'Chinhoyi', + }, + { + country_short: 'ZW', + region: 'Manicaland', + city: 'Mutare', + }, + { + country_short: 'ZW', + region: 'Midlands', + city: 'Redcliff', + }, + { + country_short: 'SL', + region: 'Western Area', + city: 'Freetown', + }, + { + country_short: 'NE', + region: 'Agadez', + city: 'Alaghsas', + }, + { + country_short: 'CF', + region: 'Bangui', + city: 'Bangui', + }, + { + country_short: 'CF', + region: 'Mambere-Kadei', + city: 'Berberati', + }, + { + country_short: 'TG', + region: 'Maritime', + city: 'Aneho', + }, + { + country_short: 'CD', + region: 'Ituri', + city: 'Bunia', + }, + { + country_short: 'CD', + region: 'Sankuru', + city: 'Kananga', + }, + { + country_short: 'CD', + region: 'Equateur', + city: 'Mbandaka', + }, + { + country_short: 'MW', + region: 'Mwanza', + city: 'Mwanza', + }, + { + country_short: 'GS', + region: 'South Georgia and the South Sandwich Islands', + city: 'King Edward Point', + }, + { + country_short: 'GA', + region: 'Estuaire', + city: 'Libreville', + }, + { + country_short: 'SS', + region: 'Unity', + city: 'Bentiu', + }, + { + country_short: 'ZW', + region: 'Masvingo', + city: 'Masvingo', + }, + { + country_short: 'GQ', + region: 'Bioko Norte', + city: 'Malabo', + }, + { + country_short: 'GQ', + region: 'Wele-Nzas', + city: 'Mongomo', + }, + { + country_short: 'GQ', + region: 'Litoral', + city: 'Bata', + }, + { + country_short: 'SC', + region: 'Anse Royale', + city: 'Anse Royale', + }, + { + country_short: 'SC', + region: 'Baie Sainte Anne', + city: "Cote d'Or", + }, + { + country_short: 'NG', + region: 'Bauchi', + city: 'Sade', + }, + { + country_short: 'CV', + region: 'Sao Domingos', + city: 'Sao Domingos', + }, + { + country_short: 'CV', + region: 'Boa Vista', + city: 'Sal Rei', + }, + { + country_short: 'SO', + region: 'Woqooyi Galbeed', + city: 'Hargeysa', + }, + { + country_short: 'SO', + region: 'Awdal', + city: 'Baki', + }, + { + country_short: 'SO', + region: 'Togdheer', + city: 'Burco', + }, + { + country_short: 'BI', + region: 'Gitega', + city: 'Gitega', + }, + { + country_short: 'BI', + region: 'Kirundo', + city: 'Kirundo', + }, + { + country_short: 'BI', + region: 'Rutana', + city: 'Rutana', + }, + { + country_short: 'BI', + region: 'Ruyigi', + city: 'Ruyigi', + }, + { + country_short: 'BI', + region: 'Cibitoke', + city: 'Cibitoke', + }, + { + country_short: 'BI', + region: 'Rumonge', + city: 'Rumonge', + }, + { + country_short: 'UG', + region: 'Mbarara', + city: 'Mbarara', + }, + { + country_short: 'KE', + region: 'Bungoma', + city: 'Webuye', + }, + { + country_short: 'KE', + region: 'Narok', + city: 'Narok', + }, + { + country_short: 'KE', + region: 'Trans Nzoia', + city: 'Kitale', + }, + { + country_short: 'KE', + region: 'Samburu', + city: 'Maralal', + }, + { + country_short: 'KE', + region: 'Mandera', + city: 'Mandera', + }, + { + country_short: 'KE', + region: 'Isiolo', + city: 'Maua', + }, + { + country_short: 'KE', + region: 'Kakamega', + city: 'Kakamega', + }, + { + country_short: 'KE', + region: 'Nandi', + city: 'Kapsabet', + }, + { + country_short: 'KE', + region: 'Nyeri', + city: 'Othaya', + }, + { + country_short: 'KE', + region: 'Migori', + city: 'Kehancha', + }, + { + country_short: 'KE', + region: 'Tharaka-Nithi', + city: 'Chuka', + }, + { + country_short: 'KE', + region: 'West Pokot', + city: 'Chepareria', + }, + { + country_short: 'KE', + region: 'Nyamira', + city: 'Keroka', + }, + { + country_short: 'KE', + region: 'Busia', + city: 'Nambale', + }, + { + country_short: 'SN', + region: 'Dakar', + city: 'Dakar', + }, + { + country_short: 'SN', + region: 'Kaffrine', + city: 'Kaffrine', + }, + { + country_short: 'SN', + region: 'Saint-Louis', + city: 'Saint-Louis', + }, + { + country_short: 'SN', + region: 'Ziguinchor', + city: 'Bignona', + }, + { + country_short: 'SN', + region: 'Diourbel', + city: 'Mbake', + }, + { + country_short: 'SN', + region: 'Tambacounda', + city: 'Tambacounda', + }, + { + country_short: 'SN', + region: 'Thies', + city: 'Mekhe', + }, + { + country_short: 'SN', + region: 'Kaolack', + city: 'Ndofane', + }, + { + country_short: 'SN', + region: 'Sedhiou', + city: 'Sedhiou', + }, + { + country_short: 'SN', + region: 'Kolda', + city: 'Kolda', + }, + { + country_short: 'SN', + region: 'Fatick', + city: 'Sokone', + }, + { + country_short: 'SN', + region: 'Louga', + city: 'Louga', + }, + { + country_short: 'SN', + region: 'Matam', + city: 'Diawara', + }, + { + country_short: 'LR', + region: 'Margibi', + city: 'Kakata', + }, + { + country_short: 'LR', + region: 'Bong', + city: 'Gbarnga', + }, + { + country_short: 'SC', + region: 'Pointe Larue', + city: 'Anse des Genets', + }, + { + country_short: 'SC', + region: 'Takamaka', + city: 'Takamaka', + }, + { + country_short: 'SC', + region: 'La Digue', + city: 'La Passe', + }, + { + country_short: 'SC', + region: 'Anse aux Pins', + city: 'Anse aux Pins', + }, + { + country_short: 'SC', + region: 'Grand Anse Mahe', + city: 'Misere', + }, + { + country_short: 'SC', + region: 'Anse Boileau', + city: 'Anse Boileau', + }, + { + country_short: 'BJ', + region: 'Zou', + city: 'Abomey', + }, + { + country_short: 'BJ', + region: 'Mono', + city: 'Lokossa', + }, + { + country_short: 'MA', + region: 'Casablanca-Settat', + city: 'Tit Mellil', + }, + { + country_short: 'MA', + region: 'Rabat-Sale-Kenitra', + city: 'Rabat', + }, + { + country_short: 'MA', + region: 'Fes- Meknes', + city: 'Skhirat', + }, + { + country_short: 'MA', + region: "L'Oriental", + city: 'Madagh', + }, + { + country_short: 'MA', + region: 'Souss-Massa', + city: 'Taghazout', + }, + { + country_short: 'MA', + region: 'Guelmim-Oued Noun (EH-partial)', + city: 'Taourirt', + }, + { + country_short: 'MA', + region: 'Draa-Tafilalet', + city: 'Jorf', + }, + { + country_short: 'KE', + region: 'Bomet', + city: 'Bomet', + }, + { + country_short: 'KE', + region: 'Baringo', + city: 'Baringo', + }, + { + country_short: 'KE', + region: 'Lamu', + city: 'Lamu', + }, + { + country_short: 'KE', + region: "Murang'a", + city: 'Maragua', + }, + { + country_short: 'KE', + region: 'Garissa', + city: 'Garissa', + }, + { + country_short: 'KE', + region: 'Marsabit', + city: 'Marsabit', + }, + { + country_short: 'KE', + region: 'Turkana', + city: 'Lodwar', + }, + { + country_short: 'CM', + region: 'Littoral', + city: 'Douala', + }, + { + country_short: 'MZ', + region: 'Gaza', + city: 'Manjacaze', + }, + { + country_short: 'MZ', + region: 'Inhambane', + city: 'Inhambane', + }, + { + country_short: 'MZ', + region: 'Manica', + city: 'Chimoio', + }, + { + country_short: 'MZ', + region: 'Zambezia', + city: 'Quelimane', + }, + { + country_short: 'MZ', + region: 'Niassa', + city: 'Lichinga', + }, + { + country_short: 'SD', + region: 'South Kordofan', + city: 'Al Fulah', + }, + { + country_short: 'SD', + region: 'West Kordofan', + city: 'Al Mijlad', + }, + { + country_short: 'SD', + region: 'Gedaref', + city: 'Al Hawatah', + }, + { + country_short: 'SD', + region: 'North Kordofan', + city: 'Barah', + }, + { + country_short: 'SD', + region: 'Sennar', + city: 'Singa', + }, + { + country_short: 'SD', + region: 'River Nile', + city: 'El Bauga', + }, + { + country_short: 'SD', + region: 'Red Sea', + city: 'Sawakin', + }, + { + country_short: 'SD', + region: 'West Darfur', + city: 'Geneina', + }, + { + country_short: 'SD', + region: 'North Darfur', + city: 'Umm Kaddadah', + }, + { + country_short: 'DZ', + region: 'Oran', + city: 'Oran', + }, + { + country_short: 'DZ', + region: 'Mascara', + city: 'Mascara', + }, + { + country_short: 'DZ', + region: 'Mostaganem', + city: 'Mostaganem', + }, + { + country_short: 'DZ', + region: 'Ain Temouchent', + city: 'El Amria', + }, + { + country_short: 'DZ', + region: 'Relizane', + city: 'Relizane', + }, + { + country_short: 'DZ', + region: 'Alger', + city: 'Algiers', + }, + { + country_short: 'DZ', + region: 'Oum el Bouaghi', + city: 'Ain Beida', + }, + { + country_short: 'DZ', + region: 'Setif', + city: 'Setif', + }, + { + country_short: 'DZ', + region: 'Naama', + city: 'Naama', + }, + { + country_short: 'DZ', + region: 'Sidi Bel Abbes', + city: 'Sfizef', + }, + { + country_short: 'DZ', + region: 'Djelfa', + city: 'Djelfa', + }, + { + country_short: 'DZ', + region: 'Tlemcen', + city: 'Chetouane', + }, + { + country_short: 'DZ', + region: 'Laghouat', + city: 'Laghouat', + }, + { + country_short: 'DZ', + region: 'Saida', + city: 'Saida', + }, + { + country_short: 'DZ', + region: 'Annaba', + city: 'Annaba', + }, + { + country_short: 'DZ', + region: 'Bechar', + city: 'Bechar', + }, + { + country_short: 'DZ', + region: 'Tindouf', + city: 'Tindouf', + }, + { + country_short: 'DZ', + region: 'Adrar', + city: 'Adrar', + }, + { + country_short: 'DZ', + region: 'Tiaret', + city: 'Tiaret', + }, + { + country_short: 'DZ', + region: 'Chlef', + city: 'Chlef', + }, + { + country_short: 'DZ', + region: 'Tissemsilt', + city: 'Tissemsilt', + }, + { + country_short: 'DZ', + region: 'Ain Defla', + city: 'Theniet el Had', + }, + { + country_short: 'DZ', + region: 'Blida', + city: 'Blida', + }, + { + country_short: 'DZ', + region: 'Tipaza', + city: 'El Affroun', + }, + { + country_short: 'DZ', + region: 'Medea', + city: "'Ain Boucif", + }, + { + country_short: 'DZ', + region: 'Ouargla', + city: 'Hassi Messaoud', + }, + { + country_short: 'DZ', + region: 'Tamanrasset', + city: 'Tamanrasset', + }, + { + country_short: 'DZ', + region: 'Ghardaia', + city: 'Berriane', + }, + { + country_short: 'DZ', + region: 'El Oued', + city: 'Robbah', + }, + { + country_short: 'DZ', + region: 'El Bayadh', + city: 'El Bayadh', + }, + { + country_short: 'DZ', + region: 'Bouira', + city: 'Sour el Ghozlane', + }, + { + country_short: 'DZ', + region: 'Boumerdes', + city: 'Boumerdas', + }, + { + country_short: 'DZ', + region: 'Tizi Ouzou', + city: 'Beni Douala', + }, + { + country_short: 'DZ', + region: 'Illizi', + city: 'Illizi', + }, + { + country_short: 'DZ', + region: 'Constantine', + city: 'Constantine', + }, + { + country_short: 'DZ', + region: 'Mila', + city: 'Telerghma', + }, + { + country_short: 'DZ', + region: 'Biskra', + city: 'Biskra', + }, + { + country_short: 'DZ', + region: 'Batna', + city: 'Batna', + }, + { + country_short: 'DZ', + region: 'Jijel', + city: 'Mechta Ouled Bou Khina', + }, + { + country_short: 'AS', + region: 'Eastern District', + city: 'Pago Pago', + }, + { + country_short: 'DZ', + region: 'Bordj Bou Arreridj', + city: 'Bordj Bou Arreridj', + }, + { + country_short: 'DZ', + region: 'Bejaia', + city: 'Akbou', + }, + { + country_short: 'EG', + region: 'Al Wadi al Jadid', + city: 'Al Kharijah', + }, + { + country_short: 'MU', + region: 'Savanne', + city: 'Chamouny', + }, + { + country_short: 'MU', + region: 'Grand Port', + city: 'Nouvelle France', + }, + { + country_short: 'MU', + region: 'Rodrigues Islands', + city: 'Le Morne', + }, + { + country_short: 'MU', + region: 'Pamplemousses', + city: 'Terre Rouge', + }, + { + country_short: 'MU', + region: 'Black River', + city: 'Flic en Flac', + }, + { + country_short: 'MU', + region: 'Riviere du Rempart', + city: 'Amaury', + }, + { + country_short: 'MU', + region: 'Flacq', + city: 'Lalmatie', + }, + { + country_short: 'NE', + region: 'Tahoua', + city: 'Tahoua', + }, + { + country_short: 'NE', + region: 'Zinder', + city: 'Zinder', + }, + { + country_short: 'NE', + region: 'Dosso', + city: 'Dogondoutchi', + }, + { + country_short: 'BW', + region: 'North West', + city: 'Maun', + }, + { + country_short: 'BF', + region: 'Houet', + city: 'Bobo-Dioulasso', + }, + { + country_short: 'BF', + region: 'Bam', + city: 'Kongoussi', + }, + { + country_short: 'BF', + region: 'Bougouriba', + city: 'Diebougou', + }, + { + country_short: 'BF', + region: 'Kenedougou', + city: 'Orodara', + }, + { + country_short: 'MR', + region: 'Nouakchott Ouest', + city: 'Nouakchott', + }, + { + country_short: 'MR', + region: 'Dakhlet Nouadhibou', + city: 'Nouadhibou', + }, + { + country_short: 'MR', + region: 'Tiris Zemmour', + city: 'Zouerate', + }, + { + country_short: 'GA', + region: 'Haut-Ogooue', + city: 'Moanda', + }, + { + country_short: 'GA', + region: 'Ogooue-Maritime', + city: 'Port-Gentil', + }, + { + country_short: 'GA', + region: 'Woleu-Ntem', + city: 'Oyem', + }, + { + country_short: 'GA', + region: 'Ogooue-Lolo', + city: 'Koulamoutou', + }, + { + country_short: 'GA', + region: 'Ngounie', + city: 'Mouila', + }, + { + country_short: 'NA', + region: 'Otjozondjupa', + city: 'Otjiwarongo', + }, + { + country_short: 'NA', + region: 'Omaheke', + city: 'Gobabis', + }, + { + country_short: 'NA', + region: 'Ohangwena', + city: 'Oshikango', + }, + { + country_short: 'NA', + region: 'Karas', + city: 'Luderitz', + }, + { + country_short: 'NA', + region: 'Kavango East', + city: 'Rundu', + }, + { + country_short: 'NA', + region: 'Oshana', + city: 'Oshakati', + }, + { + country_short: 'NA', + region: 'Zambezi', + city: 'Katima Mulilo', + }, + { + country_short: 'RW', + region: 'Ouest', + city: 'Gisenyi', + }, + { + country_short: 'RW', + region: 'Nord', + city: 'Ruhengeri', + }, + { + country_short: 'MG', + region: 'Toamasina', + city: 'Ambatondrazaka', + }, + { + country_short: 'MR', + region: 'Trarza', + city: 'Rosso', + }, + { + country_short: 'MR', + region: 'Gorgol', + city: 'Kaedi', + }, + { + country_short: 'MR', + region: 'Assaba', + city: 'Kiffa', + }, + { + country_short: 'MR', + region: 'Hodh ech Chargui', + city: 'Nema', + }, + { + country_short: 'MR', + region: 'Adrar', + city: 'Atar', + }, + { + country_short: 'MR', + region: 'Inchiri', + city: 'Akjoujt', + }, + { + country_short: 'MR', + region: 'Brakna', + city: 'Aleg', + }, + { + country_short: 'MR', + region: 'Tagant', + city: 'Tidjikja', + }, + { + country_short: 'MR', + region: 'Hodh el Gharbi', + city: 'Ayoun el Atrous', + }, + { + country_short: 'CI', + region: 'Denguele', + city: 'Odienne', + }, + { + country_short: 'CI', + region: 'Zanzan', + city: 'Tanda', + }, + { + country_short: 'CD', + region: 'Kasai Central', + city: 'Matadi', + }, + { + country_short: 'ZW', + region: 'Mashonaland Central', + city: 'Bindura', + }, + { + country_short: 'MW', + region: 'Chiradzulu', + city: 'Chiradzulu', + }, + { + country_short: 'MW', + region: 'Dowa', + city: 'Mponela', + }, + { + country_short: 'UG', + region: 'Gulu', + city: 'Gulu', + }, + { + country_short: 'UG', + region: 'Nakasongola', + city: 'Nakasongola', + }, + { + country_short: 'CM', + region: 'Ouest', + city: 'Bafoussam', + }, + { + country_short: 'UG', + region: 'Kiruhura', + city: 'Mweza', + }, + { + country_short: 'BJ', + region: 'Plateau', + city: 'Pobe', + }, + { + country_short: 'BJ', + region: 'Borgou', + city: 'Parakou', + }, + { + country_short: 'LR', + region: 'Nimba', + city: 'Sanniquellie', + }, + { + country_short: 'SC', + region: 'Baie Lazare', + city: 'Baie Lazare', + }, + { + country_short: 'BW', + region: 'Ghanzi', + city: 'Ghanzi', + }, + { + country_short: 'BW', + region: 'Chobe', + city: 'Kasane', + }, + { + country_short: 'CU', + region: 'La Habana', + city: 'Havana', + }, + { + country_short: 'KM', + region: 'Grande Comore', + city: 'Moroni', + }, + { + country_short: 'IO', + region: 'British Indian Ocean Territory', + city: 'Diego Garcia', + }, + { + country_short: 'DZ', + region: 'Guelma', + city: 'Heliopolis', + }, + { + country_short: 'DZ', + region: 'El Tarf', + city: 'El Kala', + }, + { + country_short: 'DZ', + region: 'Souk Ahras', + city: 'Souk Ahras', + }, + { + country_short: 'DZ', + region: 'Skikda', + city: 'Azzaba', + }, + { + country_short: 'DZ', + region: 'Tebessa', + city: 'Tebessa', + }, + { + country_short: 'DZ', + region: 'Khenchela', + city: 'Khenchela', + }, + { + country_short: 'CM', + region: 'Adamaoua', + city: 'Meiganga', + }, + { + country_short: 'CM', + region: 'Sud', + city: 'Kribi', + }, + { + country_short: 'CM', + region: 'Sud-Ouest', + city: 'Limbe', + }, + { + country_short: 'CM', + region: 'Nord', + city: 'Garoua', + }, + { + country_short: 'CM', + region: 'Est', + city: 'Bertoua', + }, + { + country_short: 'CM', + region: 'Nord-Ouest', + city: 'Bali', + }, + { + country_short: 'CM', + region: 'Extreme-Nord', + city: 'Maroua', + }, + { + country_short: 'UG', + region: 'Mukono', + city: 'Mukono', + }, + { + country_short: 'NG', + region: 'Ekiti', + city: 'Ifaki', + }, + { + country_short: 'BF', + region: 'Mouhoun', + city: 'Dedougou', + }, + { + country_short: 'BF', + region: 'Sanmatenga', + city: 'Kaya', + }, + { + country_short: 'BF', + region: 'Boulkiemde', + city: 'Koudougou', + }, + { + country_short: 'BF', + region: 'Yatenga', + city: 'Ouahigouya', + }, + { + country_short: 'BF', + region: 'Bale', + city: 'Boromo', + }, + { + country_short: 'BF', + region: 'Boulgou', + city: 'Tenkodogo', + }, + { + country_short: 'TG', + region: 'Kara', + city: 'Kande', + }, + { + country_short: 'TG', + region: 'Plateaux', + city: 'Atakpame', + }, + { + country_short: 'LY', + region: 'Al Wahat', + city: 'Al Burayqah', + }, + { + country_short: 'LY', + region: 'Misratah', + city: 'Bani Walid', + }, + { + country_short: 'LY', + region: 'Al Marqab', + city: 'Al Khums', + }, + { + country_short: 'SN', + region: 'Kedougou', + city: 'Kedougou', + }, + { + country_short: 'UG', + region: 'Arua', + city: 'Arua', + }, + { + country_short: 'UG', + region: 'Kyenjojo', + city: 'Kyenjojo', + }, + { + country_short: 'UG', + region: 'Zombo', + city: 'Paidha', + }, + { + country_short: 'UG', + region: 'Hoima', + city: 'Hoima', + }, + { + country_short: 'UG', + region: 'Busia', + city: 'Busia', + }, + { + country_short: 'UG', + region: 'Kabarole', + city: 'Fort Portal', + }, + { + country_short: 'UG', + region: 'Kabale', + city: 'Kabale', + }, + { + country_short: 'SZ', + region: 'Manzini', + city: 'Manzini', + }, + { + country_short: 'SZ', + region: 'Shiselweni', + city: 'Hlatikulu', + }, + { + country_short: 'KE', + region: 'Wajir', + city: 'Wajir', + }, + { + country_short: 'CV', + region: 'Mosteiros', + city: 'Porto dos Mosteiros', + }, + { + country_short: 'CV', + region: 'Tarrafal', + city: 'Tarrafal', + }, + { + country_short: 'CV', + region: 'Porto Novo', + city: 'Porto Novo', + }, + { + country_short: 'ZM', + region: 'North-Western', + city: 'Solwezi', + }, + { + country_short: 'MW', + region: 'Salima', + city: 'Salima', + }, + { + country_short: 'TD', + region: 'Ville de Ndjamena', + city: "N'Djamena", + }, + { + country_short: 'NA', + region: 'Omusati', + city: 'Outapi', + }, + { + country_short: 'AO', + region: 'Moxico', + city: 'Luena', + }, + { + country_short: 'AO', + region: 'Bengo', + city: 'Caxito', + }, + { + country_short: 'AO', + region: 'Uige', + city: 'Uige', + }, + { + country_short: 'AO', + region: 'Namibe', + city: 'Namibe', + }, + { + country_short: 'AO', + region: 'Cunene', + city: 'Ondjiva', + }, + { + country_short: 'CD', + region: 'Kasai Oriental', + city: 'Mbuji-Mayi', + }, + { + country_short: 'CD', + region: 'Sud-Kivu', + city: 'Bukavu', + }, + { + country_short: 'NG', + region: 'Benue', + city: 'Makurdi', + }, + { + country_short: 'AO', + region: 'Cuando Cubango', + city: 'Menongue', + }, + { + country_short: 'AO', + region: 'Lunda-Norte', + city: 'Lucapa', + }, + { + country_short: 'GH', + region: 'Bono East', + city: 'Techiman', + }, + { + country_short: 'UG', + region: 'Iganga', + city: 'Iganga', + }, + { + country_short: 'UG', + region: 'Mubende', + city: 'Mubende', + }, + { + country_short: 'UG', + region: 'Kanungu', + city: 'Ntungamo', + }, + { + country_short: 'UG', + region: 'Butaleja', + city: 'Butaleja', + }, + { + country_short: 'MW', + region: 'Mangochi', + city: 'Mangochi', + }, + { + country_short: 'MW', + region: 'Thyolo', + city: 'Thyolo', + }, + { + country_short: 'MW', + region: 'Nkhotakota', + city: 'Nkhotakota', + }, + { + country_short: 'ML', + region: 'Mopti', + city: 'Mopti', + }, + { + country_short: 'AO', + region: 'Zaire', + city: 'Soio', + }, + { + country_short: 'UG', + region: 'Masaka', + city: 'Masaka', + }, + { + country_short: 'AO', + region: 'Cuanza-Norte', + city: "N'dalatando", + }, + { + country_short: 'GH', + region: 'Ahafo', + city: 'Goaso', + }, + { + country_short: 'BW', + region: 'Kgalagadi', + city: 'Tshabong', + }, + { + country_short: 'SO', + region: 'Bari', + city: 'Bosaso', + }, + { + country_short: 'CF', + region: 'Ouaka', + city: 'Ippy', + }, + { + country_short: 'TN', + region: 'Le Kef', + city: 'El Kef', + }, + { + country_short: 'TN', + region: 'Kasserine', + city: 'Kasserine', + }, + { + country_short: 'MG', + region: 'Fianarantsoa', + city: 'Ambositra', + }, + { + country_short: 'CD', + region: 'Kwilu', + city: 'Bandundu', + }, + { + country_short: 'CD', + region: 'Tanganyika', + city: 'Kalemie', + }, + { + country_short: 'MA', + region: 'Laayoune-Sakia El Hamra (EH-partial)', + city: 'Tarfaya', + }, + { + country_short: 'LY', + region: 'Sabha', + city: 'Sabha', + }, + { + country_short: 'LY', + region: "Wadi ash Shati'", + city: 'Brak', + }, + { + country_short: 'LY', + region: 'Az Zawiyah', + city: 'Surman', + }, + { + country_short: 'LY', + region: 'Al Jabal al Gharbi', + city: 'Gharyan', + }, + { + country_short: 'LY', + region: 'Surt', + city: 'Sirte', + }, + { + country_short: 'LY', + region: 'Al Jabal al Akhdar', + city: "Al Bayda'", + }, + { + country_short: 'LY', + region: 'Al Marj', + city: 'Al Abyar', + }, + { + country_short: 'LY', + region: 'Al Jufrah', + city: 'Hun', + }, + { + country_short: 'LY', + region: 'Al Kufrah', + city: 'At Taj', + }, + { + country_short: 'LY', + region: 'Darnah', + city: 'Darnah', + }, + { + country_short: 'LY', + region: 'Al Butnan', + city: 'Tobruk', + }, + { + country_short: 'LY', + region: 'Al Jafarah', + city: "Al 'Aziziyah", + }, + { + country_short: 'SH', + region: ' Ascension and Tristan Da Cunha"', + city: 'Saint Helena', + }, + { + country_short: 'IN', + region: 'Dadra and Nagar Haveli', + city: 'Silvassa', + }, + { + country_short: 'IN', + region: 'Dadra and Nagar Haveli and Daman and Diu', + city: 'Daman', + }, + { + country_short: 'MY', + region: 'Wilayah Persekutuan Labuan', + city: 'Victoria', + }, + { + country_short: 'MY', + region: 'Perlis', + city: 'Arau', + }, + { + country_short: 'LA', + region: 'Savannakhet', + city: 'Savannakhet', + }, + { + country_short: 'LA', + region: 'Attapu', + city: 'Attapeu', + }, + { + country_short: 'LA', + region: 'Khammouan', + city: 'Thakhek', + }, + { + country_short: 'MM', + region: 'Magway', + city: 'Magway', + }, + { + country_short: 'NZ', + region: 'Taranaki', + city: 'Douglas', + }, + { + country_short: 'NC', + region: 'Province Nord', + city: 'Kaala-Gomen', + }, + { + country_short: 'LK', + region: 'Central Province', + city: 'Kandy', + }, + { + country_short: 'NP', + region: 'Mahakali', + city: 'Mahendranagar', + }, + { + country_short: 'NR', + region: 'Yaren', + city: 'Yaren', + }, + { + country_short: 'MV', + region: 'South Miladhunmadulu', + city: 'Manadhoo', + }, + { + country_short: 'WS', + region: 'Tuamasaga', + city: 'Apia', + }, + { + country_short: 'NZ', + region: 'Southland', + city: 'Invercargill', + }, + { + country_short: 'MM', + region: 'Nay Pyi Taw', + city: 'Nay Pyi Taw', + }, + { + country_short: 'GU', + region: 'Hagatna', + city: 'Hagatna', + }, + { + country_short: 'FM', + region: 'Pohnpei', + city: 'Palikir - National Government Center', + }, + { + country_short: 'FM', + region: 'Kosrae', + city: 'Lelu', + }, + { + country_short: 'PF', + region: 'Iles du Vent', + city: 'Papeete', + }, + { + country_short: 'LA', + region: 'Champasak', + city: 'Pakse', + }, + { + country_short: 'KH', + region: 'Kampong Chhnang', + city: 'Kampong Chhnang', + }, + { + country_short: 'TO', + region: "Ha'apai", + city: 'Pangai', + }, + { + country_short: 'AT', + region: 'Vorarlberg', + city: 'Feldkirch', + }, + { + country_short: 'HN', + region: 'La Paz', + city: 'Marcala', + }, + { + country_short: 'HN', + region: 'Ocotepeque', + city: 'Sensenti', + }, + { + country_short: 'HN', + region: 'Cortes', + city: 'San Pedro Sula', + }, + { + country_short: 'AR', + region: 'Rio Negro', + city: 'San Antonio Oeste', + }, + { + country_short: 'HN', + region: 'Francisco Morazan', + city: 'Tegucigalpa', + }, + { + country_short: 'HN', + region: 'Comayagua', + city: 'Siguatepeque', + }, + { + country_short: 'CL', + region: 'La Araucania', + city: 'Villarrica', + }, + { + country_short: 'EC', + region: 'Manabi', + city: 'Manta', + }, + { + country_short: 'AR', + region: 'Santa Cruz', + city: 'Perito Moreno', + }, + { + country_short: 'BR', + region: 'Rio Grande do Norte', + city: 'Areia Branca', + }, + { + country_short: 'AR', + region: 'Misiones', + city: 'Puerto Eldorado', + }, + { + country_short: 'BR', + region: 'Sergipe', + city: 'Aracaju', + }, + { + country_short: 'PE', + region: 'Loreto', + city: 'Yurimaguas', + }, + { + country_short: 'PE', + region: 'Puno', + city: 'La Rinconada', + }, + { + country_short: 'HN', + region: 'Atlantida', + city: 'La Ceiba', + }, + { + country_short: 'HN', + region: 'Islas de la Bahia', + city: 'Roatan', + }, + { + country_short: 'GT', + region: 'Alta Verapaz', + city: 'Tucuru', + }, + { + country_short: 'GT', + region: 'Quetzaltenango', + city: 'Coatepeque', + }, + { + country_short: 'GT', + region: 'Chiquimula', + city: 'Chiquimula', + }, + { + country_short: 'GT', + region: 'Escuintla', + city: 'Nueva Concepcion', + }, + { + country_short: 'GT', + region: 'Izabal', + city: 'Morales', + }, + { + country_short: 'AR', + region: 'Jujuy', + city: 'San Salvador de Jujuy', + }, + { + country_short: 'BR', + region: 'Roraima', + city: 'Baliza', + }, + { + country_short: 'NI', + region: 'Rivas', + city: 'San Jorge', + }, + { + country_short: 'NI', + region: 'Carazo', + city: 'Santa Teresa', + }, + { + country_short: 'AR', + region: 'Tucuman', + city: 'San Miguel de Tucuman', + }, + { + country_short: 'MX', + region: 'Sinaloa', + city: 'Los Mochis', + }, + { + country_short: 'CH', + region: 'Graubunden', + city: 'Chur', + }, + { + country_short: 'KN', + region: 'Saint Paul Charlestown', + city: 'Charlestown', + }, + { + country_short: 'JM', + region: 'Saint James', + city: 'Montego Bay', + }, + { + country_short: 'MS', + region: 'Saint Anthony', + city: 'Plymouth', + }, + { + country_short: 'TC', + region: 'Turks and Caicos Islands', + city: 'Cockburn Town', + }, + { + country_short: 'PH', + region: 'Camarines Sur', + city: 'San Miguel', + }, + { + country_short: 'CO', + region: 'Magdalena', + city: 'Santa Marta', + }, + { + country_short: 'CO', + region: 'Boyaca', + city: 'Duitama', + }, + { + country_short: 'PE', + region: 'Junin', + city: 'Jauja', + }, + { + country_short: 'PE', + region: 'La Libertad', + city: 'Trujillo', + }, + { + country_short: 'MD', + region: 'Drochia', + city: 'Drochia', + }, + { + country_short: 'AR', + region: 'Formosa', + city: 'El Colorado', + }, + { + country_short: 'EC', + region: 'Imbabura', + city: 'Cotacachi', + }, + { + country_short: 'EC', + region: 'Chimborazo', + city: 'Riobamba', + }, + { + country_short: 'EC', + region: 'Carchi', + city: 'El Angel', + }, + { + country_short: 'BZ', + region: 'Belize', + city: 'San Pedro', + }, + { + country_short: 'EC', + region: 'Canar', + city: 'Azogues', + }, + { + country_short: 'EC', + region: 'Morona Santiago', + city: 'Gualaquiza', + }, + { + country_short: 'EC', + region: 'Bolivar', + city: 'San Miguel', + }, + { + country_short: 'EC', + region: 'Loja', + city: 'Loja', + }, + { + country_short: 'CW', + region: 'Curacao', + city: 'Willemstad', + }, + { + country_short: 'CO', + region: 'Caqueta', + city: 'Florencia', + }, + { + country_short: 'EC', + region: 'Orellana', + city: 'La Joya de los Sachas', + }, + { + country_short: 'EC', + region: 'Esmeraldas', + city: 'San Lorenzo de Esmeraldas', + }, + { + country_short: 'MT', + region: 'Gzira', + city: 'Gzira', + }, + { + country_short: 'PH', + region: 'Cagayan', + city: 'Claveria', + }, + { + country_short: 'FJ', + region: 'Eastern', + city: 'Waidradranu Settlement', + }, + { + country_short: 'LK', + region: 'North Western Province', + city: 'Kurunegala', + }, + { + country_short: 'LK', + region: 'Southern Province', + city: 'Tangalle', + }, + { + country_short: 'LV', + region: 'Kekavas novads', + city: 'Kekava', + }, + { + country_short: 'UZ', + region: 'Jizzax', + city: 'Dustlik', + }, + { + country_short: 'LB', + region: 'Aakkar', + city: 'Halba', + }, + { + country_short: 'AZ', + region: 'Agcabadi', + city: 'Agdzhabedy', + }, + { + country_short: 'PN', + region: 'Pitcairn', + city: 'Adamstown', + }, + { + country_short: 'CC', + region: 'Cocos (Keeling) Islands', + city: 'West Island', + }, + { + country_short: 'NF', + region: 'Norfolk Island', + city: 'Kingston', + }, + { + country_short: 'NU', + region: 'Niue', + city: 'Alofi', + }, + { + country_short: 'CX', + region: 'Christmas Island', + city: 'Flying Fish Cove', + }, + { + country_short: 'FK', + region: 'Falkland Islands (Malvinas)', + city: 'Stanley', + }, + { + country_short: 'TV', + region: 'Funafuti', + city: 'Funafuti', + }, + { + country_short: 'PW', + region: 'Melekeok', + city: 'Melekeok - Palau State Capital', + }, + { + country_short: 'AX', + region: 'Mariehamn', + city: 'Mariehamn', + }, + { + country_short: 'SX', + region: 'Sint Maarten (Dutch Part)', + city: 'Philipsburg', + }, + { + country_short: 'MH', + region: 'Majuro', + city: 'Majuro', + }, + { + country_short: 'KI', + region: 'Gilbert Islands', + city: 'Tarawa', + }, + { + country_short: 'SI', + region: 'Novo Mesto', + city: 'Novo Mesto', + }, + { + country_short: 'LB', + region: 'Nabatiye', + city: 'Hasbaiya', + }, + { + country_short: 'BO', + region: 'Chuquisaca', + city: 'Sucre', + }, + { + country_short: 'SI', + region: 'Litija', + city: 'Litija', + }, + { + country_short: 'IE', + region: 'Laois', + city: 'Durrow', + }, + { + country_short: 'MT', + region: 'Rabat Malta', + city: 'Rabat', + }, + { + country_short: 'PY', + region: 'Alto Parana', + city: 'Hernandarias', + }, + { + country_short: 'CR', + region: 'Guanacaste', + city: 'Juntas', + }, + { + country_short: 'CL', + region: "Libertador General Bernardo O'Higgins", + city: 'Chimbarongo', + }, + { + country_short: 'CL', + region: 'Coquimbo', + city: 'Illapel', + }, + { + country_short: 'CL', + region: 'Nuble', + city: 'Quillon', + }, + { + country_short: 'PY', + region: 'Amambay', + city: 'Pedro Juan Caballero', + }, + { + country_short: 'PY', + region: 'San Pedro', + city: 'Nueva Germania', + }, + { + country_short: 'EC', + region: 'Santa Elena', + city: 'Santa Elena', + }, + { + country_short: 'DO', + region: 'La Altagracia', + city: 'Uvero Alto', + }, + { + country_short: 'MX', + region: 'Yucatan', + city: 'Merida', + }, + { + country_short: 'GT', + region: 'Quiche', + city: 'Santa Cruz del Quiche', + }, + { + country_short: 'GT', + region: 'Solola', + city: 'Santa Lucia Utatlan', + }, + { + country_short: 'DO', + region: 'La Romana', + city: 'La Romana', + }, + { + country_short: 'SV', + region: 'Sonsonate', + city: 'Acajutla', + }, + { + country_short: 'VE', + region: 'Miranda', + city: 'San Antonio de Los Altos', + }, + { + country_short: 'HN', + region: 'Santa Barbara', + city: 'Sula', + }, + { + country_short: 'HN', + region: 'Lempira', + city: 'Gracias', + }, + { + country_short: 'MX', + region: 'Nayarit', + city: 'Bucerias', + }, + { + country_short: 'CO', + region: 'Cordoba', + city: 'Monteria', + }, + { + country_short: 'VE', + region: 'Monagas', + city: 'Maturin', + }, + { + country_short: 'PE', + region: 'Ica', + city: 'Chanchamayo', + }, + { + country_short: 'CO', + region: 'Putumayo', + city: 'La Hormiga', + }, + { + country_short: 'PY', + region: 'Itapua', + city: 'Obligado', + }, + { + country_short: 'HN', + region: 'El Paraiso', + city: 'Teupasenti', + }, + { + country_short: 'PE', + region: 'Piura', + city: 'Piura', + }, + { + country_short: 'PY', + region: 'Canindeyu', + city: 'Colonia Catuete', + }, + { + country_short: 'PY', + region: 'Guaira', + city: 'Villarrica', + }, + { + country_short: 'NI', + region: 'Chontales', + city: 'Juigalpa', + }, + { + country_short: 'CO', + region: 'Guaviare', + city: 'San Jose del Guaviare', + }, + { + country_short: 'EC', + region: 'Sucumbios', + city: 'Lago Agrio', + }, + { + country_short: 'CO', + region: 'San Andres, Providencia y Santa Catalina', + city: 'San Andres, Providencia y Santa Catalina', + }, + { + country_short: 'PE', + region: 'El Callao', + city: 'Callao', + }, + { + country_short: 'GT', + region: 'Jutiapa', + city: 'Jutiapa', + }, + { + country_short: 'VE', + region: 'Amazonas', + city: 'Puerto Ayacucho', + }, + { + country_short: 'MX', + region: 'Oaxaca', + city: 'San Jose Chiltepec', + }, + { + country_short: 'HN', + region: 'Colon', + city: 'Saba', + }, + { + country_short: 'DO', + region: 'San Jose de Ocoa', + city: 'Banilejo', + }, + { + country_short: 'DO', + region: 'El Seibo', + city: 'Santa Cruz de El Seibo', + }, + { + country_short: 'PE', + region: 'Pasco', + city: 'Yanahuanca', + }, + { + country_short: 'CO', + region: 'Arauca', + city: 'Arauquita', + }, + { + country_short: 'CO', + region: 'La Guajira', + city: 'Panama', + }, + { + country_short: 'PY', + region: 'Concepcion', + city: 'Horqueta', + }, + { + country_short: 'NI', + region: 'Costa Caribe Sur', + city: 'Bluefields', + }, + { + country_short: 'AR', + region: 'Catamarca', + city: 'San Antonio', + }, + { + country_short: 'BO', + region: 'El Beni', + city: 'La Santisima Trinidad', + }, + { + country_short: 'GT', + region: 'Sacatepequez', + city: 'Antigua Guatemala', + }, + { + country_short: 'TT', + region: 'Mayaro-Rio Claro', + city: 'Rio Claro', + }, + { + country_short: 'VE', + region: 'Portuguesa', + city: 'Acarigua', + }, + { + country_short: 'PE', + region: 'Lambayeque', + city: 'Chiclayo', + }, + { + country_short: 'VE', + region: 'Yaracuy', + city: 'Yaritagua', + }, + { + country_short: 'VE', + region: 'Merida', + city: 'Merida', + }, + { + country_short: 'GT', + region: 'Suchitepequez', + city: 'Mazatenango', + }, + { + country_short: 'VE', + region: 'Tachira', + city: 'San Cristobal', + }, + { + country_short: 'PE', + region: 'Tacna', + city: 'Tacna', + }, + { + country_short: 'EC', + region: 'Napo', + city: 'Tena', + }, + { + country_short: 'CL', + region: 'Atacama', + city: 'Copiapo', + }, + { + country_short: 'BO', + region: 'Pando', + city: 'Cobija', + }, + { + country_short: 'CR', + region: 'Limon', + city: 'Cahuita', + }, + { + country_short: 'PE', + region: 'Moquegua', + city: 'Torata', + }, + { + country_short: 'PY', + region: 'Boqueron', + city: 'Filadelfia', + }, + { + country_short: 'PE', + region: 'Madre de Dios', + city: 'Puerto Maldonado', + }, + { + country_short: 'DO', + region: 'Dajabon', + city: 'Loma de Cabrera', + }, + { + country_short: 'BZ', + region: 'Corozal', + city: 'Corozal', + }, + { + country_short: 'TT', + region: 'Penal-Debe', + city: 'Penal', + }, + { + country_short: 'TT', + region: 'Princes Town', + city: 'Princes Town', + }, + { + country_short: 'EC', + region: 'Pastaza', + city: 'Pichincha', + }, + { + country_short: 'PA', + region: 'Los Santos', + city: 'Las Tablas', + }, + { + country_short: 'NI', + region: 'Matagalpa', + city: 'Rio Blanco', + }, + { + country_short: 'CL', + region: 'Magallanes', + city: 'Punta Arenas', + }, + { + country_short: 'CL', + region: 'Aisen del General Carlos Ibanez del Campo', + city: 'El Carmen', + }, + { + country_short: 'PH', + region: 'Dinagat Islands', + city: 'Arellano', + }, + { + country_short: 'MM', + region: 'Tanintharyi', + city: 'Myeik', + }, + { + country_short: 'IE', + region: 'Sligo', + city: 'Sligo', + }, + { + country_short: 'IE', + region: 'Westmeath', + city: 'An Muileann gCearr', + }, + { + country_short: 'BG', + region: 'Veliko Tarnovo', + city: 'Svishtov', + }, + { + country_short: 'BG', + region: 'Silistra', + city: 'Dulovo', + }, + { + country_short: 'BG', + region: 'Vidin', + city: 'Kula', + }, + { + country_short: 'BG', + region: 'Razgrad', + city: 'Razgrad', + }, + { + country_short: 'MT', + region: 'Fgura', + city: 'Fgura', + }, + { + country_short: 'MT', + region: 'Zejtun', + city: 'Zejtun', + }, + { + country_short: 'MT', + region: 'Marsaskala', + city: "Ta' Monita", + }, + { + country_short: 'MT', + region: 'Paola', + city: 'Paola', + }, + { + country_short: 'MT', + region: 'Ghaxaq', + city: 'Ghaxaq', + }, + { + country_short: 'MT', + region: 'Siggiewi', + city: 'Siggiewi', + }, + { + country_short: 'MT', + region: 'Mtarfa', + city: 'Imtarfa', + }, + { + country_short: 'MT', + region: 'Isla', + city: 'Senglea', + }, + { + country_short: 'MT', + region: 'Floriana', + city: 'Floriana', + }, + { + country_short: 'MT', + region: "Saint Lucia's", + city: 'Santa Lucija', + }, + { + country_short: 'MT', + region: 'Mellieha', + city: 'Mellieha', + }, + { + country_short: 'MT', + region: 'Luqa', + city: 'Luqa', + }, + { + country_short: 'MT', + region: 'Marsa', + city: 'Marsa', + }, + { + country_short: 'MT', + region: "Saint Julian's", + city: 'San Giljan', + }, + { + country_short: 'MT', + region: 'Tarxien', + city: 'Tarxien', + }, + { + country_short: 'MT', + region: 'Santa Venera', + city: 'Santa Venera', + }, + { + country_short: 'MT', + region: 'Zurrieq', + city: 'Zurrieq', + }, + { + country_short: 'MT', + region: 'Naxxar', + city: 'Naxxar', + }, + { + country_short: 'MT', + region: 'Mqabba', + city: 'Mqabba', + }, + { + country_short: 'MT', + region: 'Gharghur', + city: 'Hal Gharghur', + }, + { + country_short: 'MT', + region: 'Rabat Gozo', + city: 'Victoria', + }, + { + country_short: 'CH', + region: 'Uri', + city: 'Andermatt', + }, + { + country_short: 'SI', + region: 'Polzela', + city: 'Polzela', + }, + { + country_short: 'LV', + region: 'Daugavpils', + city: 'Daugavpils', + }, + { + country_short: 'LB', + region: 'Liban-Nord', + city: 'Bcharre', + }, + { + country_short: 'TJ', + region: 'Nohiyahoi Tobei Jumhuri', + city: 'Obigarm', + }, + { + country_short: 'SI', + region: 'Slovenj Gradec', + city: 'Slovenj Gradec', + }, + { + country_short: 'LV', + region: 'Gulbenes novads', + city: 'Gulbene', + }, + { + country_short: 'LV', + region: 'Ropazu novads', + city: 'Ulbroka', + }, + { + country_short: 'MK', + region: 'Radovis', + city: 'Radovis', + }, + { + country_short: 'ME', + region: 'Niksic', + city: 'Niksic', + }, + { + country_short: 'BG', + region: 'Lovech', + city: 'Troyan', + }, + { + country_short: 'LT', + region: 'Taurages apskritis', + city: 'Taurage', + }, + { + country_short: 'RS', + region: 'Severnobanatski okrug', + city: 'Gornji Breg', + }, + { + country_short: 'RS', + region: 'Zapadnobacki okrug', + city: 'Apatin', + }, + { + country_short: 'BY', + region: "Mahilyowskaya voblasts'", + city: 'Mahilyow', + }, + { + country_short: 'SI', + region: 'Brda', + city: 'Dobrovo', + }, + { + country_short: 'SI', + region: 'Sempeter-Vrtojba', + city: 'Sempeter pri Gorici', + }, + { + country_short: 'SI', + region: 'Nova Gorica', + city: 'Nova Gorica', + }, + { + country_short: 'MD', + region: 'Stefan Voda', + city: 'Stefan Voda', + }, + { + country_short: 'MD', + region: 'Soroca', + city: 'Soroca', + }, + { + country_short: 'MD', + region: 'Leova', + city: 'Iargara', + }, + { + country_short: 'MD', + region: 'Calarasi', + city: 'Calarasi', + }, + { + country_short: 'MD', + region: 'Basarabeasca', + city: 'Basarabeasca', + }, + { + country_short: 'MD', + region: 'Dubasari', + city: 'Cocieri', + }, + { + country_short: 'SY', + region: 'Al Hasakah', + city: 'Al Qamishli', + }, + { + country_short: 'RO', + region: 'Satu Mare', + city: 'Satu Mare', + }, + { + country_short: 'LV', + region: 'Ogres novads', + city: 'Jumprava', + }, + { + country_short: 'LV', + region: 'Madonas novads', + city: 'Lubana', + }, + { + country_short: 'LV', + region: 'Aizkraukles novads', + city: 'Jaunjelgava', + }, + { + country_short: 'LV', + region: 'Jekabpils novads', + city: 'Jekabpils', + }, + { + country_short: 'LV', + region: 'Marupes novads', + city: 'Pinki', + }, + { + country_short: 'LV', + region: 'Olaines novads', + city: 'Olaine', + }, + { + country_short: 'LV', + region: 'Jelgavas novads', + city: 'Jelgava', + }, + { + country_short: 'LV', + region: 'Bauskas novads', + city: 'Iecava', + }, + { + country_short: 'LV', + region: 'Dobeles novads', + city: 'Dobele', + }, + { + country_short: 'LV', + region: 'Salaspils novads', + city: 'Salaspils', + }, + { + country_short: 'LV', + region: 'Saldus novads', + city: 'Saldus', + }, + { + country_short: 'LV', + region: 'Tukuma novads', + city: 'Tukums', + }, + { + country_short: 'LV', + region: 'Jurmala', + city: 'Jurmala', + }, + { + country_short: 'LV', + region: 'Jelgava', + city: 'Tireli', + }, + { + country_short: 'LV', + region: 'Adazu novads', + city: 'Carnikava', + }, + { + country_short: 'LV', + region: 'Siguldas novads', + city: 'Sigulda', + }, + { + country_short: 'LV', + region: 'Talsu novads', + city: 'Stende', + }, + { + country_short: 'LV', + region: 'Ventspils novads', + city: 'Ventspils', + }, + { + country_short: 'LV', + region: 'Liepaja', + city: 'Liepaja', + }, + { + country_short: 'LV', + region: 'Dienvidkurzemes novads', + city: 'Priekule', + }, + { + country_short: 'LV', + region: 'Rezeknes novads', + city: 'Rezekne', + }, + { + country_short: 'LV', + region: 'Cesu novads', + city: 'Cesis', + }, + { + country_short: 'LV', + region: 'Valmieras novads', + city: 'Valmiera', + }, + { + country_short: 'LV', + region: 'Smiltenes novads', + city: 'Smiltene', + }, + { + country_short: 'SI', + region: 'Kanal', + city: 'Kanal', + }, + { + country_short: 'SI', + region: 'Destrnik', + city: 'Crna na Koroskem', + }, + { + country_short: 'SI', + region: 'Sentrupert', + city: 'Bistrica', + }, + { + country_short: 'SI', + region: 'Brezovica', + city: 'Brezovica', + }, + { + country_short: 'SI', + region: 'Skocjan', + city: 'Jelendol', + }, + { + country_short: 'SI', + region: 'Trebnje', + city: 'Trebnje', + }, + { + country_short: 'SI', + region: 'Semic', + city: 'Semic', + }, + { + country_short: 'SI', + region: 'Crensovci', + city: 'Crensovci', + }, + { + country_short: 'CH', + region: 'Nidwalden', + city: 'Stans', + }, + { + country_short: 'SI', + region: 'Ljutomer', + city: 'Ljutomer', + }, + { + country_short: 'SI', + region: 'Sveti Tomaz', + city: 'Trnovci', + }, + { + country_short: 'SI', + region: 'Krizevci', + city: 'Gajsevci', + }, + { + country_short: 'SI', + region: 'Ptuj', + city: 'Ptuj', + }, + { + country_short: 'SI', + region: 'Dornava', + city: 'Cunkovci', + }, + { + country_short: 'SI', + region: 'Markovci', + city: 'Markovci', + }, + { + country_short: 'SI', + region: 'Vrhnika', + city: 'Vrhnika', + }, + { + country_short: 'SI', + region: 'Krsko', + city: 'Krsko', + }, + { + country_short: 'SI', + region: 'Zagorje ob Savi', + city: 'Zagorje ob Savi', + }, + { + country_short: 'SI', + region: 'Ajdovscina', + city: 'Beltinci', + }, + { + country_short: 'SI', + region: 'Ormoz', + city: 'Ormoz', + }, + { + country_short: 'ME', + region: 'Pljevlja', + city: 'Pljevlja', + }, + { + country_short: 'ME', + region: 'Pluzine', + city: 'Pluzine', + }, + { + country_short: 'ME', + region: 'Danilovgrad', + city: 'Danilovgrad', + }, + { + country_short: 'ME', + region: 'Zabljak', + city: 'Zabljak', + }, + { + country_short: 'YE', + region: 'Dhamar', + city: 'Zarajah', + }, + { + country_short: 'YE', + region: "Ad Dali'", + city: 'Dhalie', + }, + { + country_short: 'SI', + region: 'Sevnica', + city: 'Sevnica', + }, + { + country_short: 'SI', + region: 'Radenci', + city: 'Radenci', + }, + { + country_short: 'SI', + region: 'Ilirska Bistrica', + city: 'Ilirska Bistrica', + }, + { + country_short: 'SI', + region: 'Hrpelje-Kozina', + city: 'Tatre', + }, + { + country_short: 'SI', + region: 'Duplek', + city: 'Metava', + }, + { + country_short: 'SI', + region: 'Kosanjevica na Krki', + city: 'Jablance', + }, + { + country_short: 'SI', + region: 'Sveti Jurij v Slovenskih goricah', + city: 'Zgornji Gasteraj', + }, + { + country_short: 'SI', + region: 'Smarje pri Jelsah', + city: 'Sotensko pri Smarju', + }, + { + country_short: 'SI', + region: 'Crnomelj', + city: 'Crnomelj', + }, + { + country_short: 'SI', + region: 'Sveta Trojica v Slovenskih goricah', + city: 'Nadbisec', + }, + { + country_short: 'SI', + region: 'Smarjeske Toplice', + city: 'Smarjeta', + }, + { + country_short: 'SI', + region: 'Bistrica ob Sotli', + city: 'Bistrica ob Sotli', + }, + { + country_short: 'SI', + region: 'Smartno pri Litiji', + city: 'Vintarjevec', + }, + { + country_short: 'SI', + region: 'Sentilj', + city: 'Sentilj v Slovenskih goricah', + }, + { + country_short: 'SI', + region: 'Brezice', + city: 'Brezice', + }, + { + country_short: 'SI', + region: 'Vipava', + city: 'Vipava', + }, + { + country_short: 'SI', + region: 'Ziri', + city: 'Ziri', + }, + { + country_short: 'AD', + region: 'Escaldes-Engordany', + city: 'Les Escaldes', + }, + { + country_short: 'AD', + region: 'La Massana', + city: 'La Massana', + }, + { + country_short: 'AD', + region: 'Encamp', + city: 'Pas de la Casa', + }, + { + country_short: 'AD', + region: 'Canillo', + city: 'El Tarter', + }, + { + country_short: 'SY', + region: 'Ar Raqqah', + city: 'As Sabkhah', + }, + { + country_short: 'MK', + region: 'Probistip', + city: 'Probishtip', + }, + { + country_short: 'MK', + region: 'Sveti Nikole', + city: 'Sveti Nikole', + }, + { + country_short: 'MK', + region: 'Novo Selo', + city: 'Novo Selo', + }, + { + country_short: 'MK', + region: 'Bosilovo', + city: 'Bosilovo', + }, + { + country_short: 'MK', + region: 'Valandovo', + city: 'Valandovo', + }, + { + country_short: 'MK', + region: 'Gevgelija', + city: 'Gevgelija', + }, + { + country_short: 'MK', + region: 'Pehcevo', + city: 'Pehcevo', + }, + { + country_short: 'MK', + region: 'Berovo', + city: 'Berovo', + }, + { + country_short: 'MK', + region: 'Prilep', + city: 'Prilep', + }, + { + country_short: 'MK', + region: 'Gostivar', + city: 'Chegrane', + }, + { + country_short: 'MK', + region: 'Tearce', + city: 'Slatino', + }, + { + country_short: 'MK', + region: 'Butel', + city: 'Radishani', + }, + { + country_short: 'MK', + region: 'Resen', + city: 'Grncari', + }, + { + country_short: 'MK', + region: 'Gazi Baba', + city: 'Creshevo', + }, + { + country_short: 'MK', + region: 'Mavrovo i Rostusa', + city: 'Rostusha', + }, + { + country_short: 'MK', + region: 'Studenicani', + city: 'Studenichani', + }, + { + country_short: 'MK', + region: 'Zelino', + city: 'Larce', + }, + { + country_short: 'MK', + region: 'Stip', + city: 'Shtip', + }, + { + country_short: 'MK', + region: 'Kicevo', + city: 'Zajas', + }, + { + country_short: 'LU', + region: 'Mersch', + city: 'Bissen', + }, + { + country_short: 'FO', + region: 'Eysturoy', + city: 'Fuglafjordur', + }, + { + country_short: 'SI', + region: 'Store', + city: 'Store', + }, + { + country_short: 'SI', + region: 'Cerklje na Gorenjskem', + city: 'Zgornji Brnik', + }, + { + country_short: 'SI', + region: 'Dobrepolje', + city: 'Cesta', + }, + { + country_short: 'AL', + region: 'Fier', + city: 'Fier', + }, + { + country_short: 'IE', + region: 'Monaghan', + city: 'Monaghan', + }, + { + country_short: 'IN', + region: 'Andaman and Nicobar Islands', + city: 'Port Blair', + }, + { + country_short: 'NZ', + region: 'Nelson', + city: 'Nelson', + }, + { + country_short: 'NZ', + region: 'Marlborough', + city: 'Blenheim', + }, + { + country_short: 'NP', + region: 'Dhawalagiri', + city: 'Chitre', + }, + { + country_short: 'GU', + region: 'Tamuning-Tumon-Harmon', + city: 'Tamuning-Tumon-Harmon Village', + }, + { + country_short: 'PH', + region: 'Romblon', + city: 'Odiongan', + }, + { + country_short: 'PH', + region: 'Masbate', + city: 'Mobo', + }, + { + country_short: 'PH', + region: 'Marinduque', + city: 'Tigui', + }, + { + country_short: 'PH', + region: 'Catanduanes', + city: 'Calatagan', + }, + { + country_short: 'PH', + region: 'Southern Leyte', + city: 'Ibarra', + }, + { + country_short: 'PH', + region: 'Sultan Kudarat', + city: 'Tacurong', + }, + { + country_short: 'PH', + region: 'South Cotabato', + city: 'Glamang', + }, + { + country_short: 'PH', + region: 'Surigao del Norte', + city: 'Mabua', + }, + { + country_short: 'PH', + region: 'Maguindanao', + city: 'Sultan Kudarat', + }, + { + country_short: 'PH', + region: 'Northern Samar', + city: 'Catarman', + }, + { + country_short: 'PH', + region: 'Eastern Samar', + city: 'Dolores', + }, + { + country_short: 'PH', + region: 'Davao Oriental', + city: 'Sigaboy', + }, + { + country_short: 'PH', + region: 'Sarangani', + city: 'Camasi', + }, + { + country_short: 'PH', + region: 'Camiguin', + city: 'Pandan', + }, + { + country_short: 'PH', + region: 'La Union', + city: 'Acao', + }, + { + country_short: 'PH', + region: 'Ilocos Sur', + city: 'Vigan', + }, + { + country_short: 'PH', + region: 'Ifugao', + city: 'Potia', + }, + { + country_short: 'PH', + region: 'Mountain Province', + city: 'Bontoc', + }, + { + country_short: 'PH', + region: 'Ilocos Norte', + city: 'Baay', + }, + { + country_short: 'PH', + region: 'Kalinga', + city: 'Laya', + }, + { + country_short: 'PH', + region: 'Abra', + city: 'Bangued', + }, + { + country_short: 'PH', + region: 'Nueva Vizcaya', + city: 'Bayombong', + }, + { + country_short: 'PH', + region: 'Quirino', + city: 'Cabarroguis', + }, + { + country_short: 'NP', + region: 'Karnali', + city: 'Jumla', + }, + { + country_short: 'CA', + region: 'Yukon', + city: 'Whitehorse', + }, + { + country_short: 'CA', + region: 'Nunavut', + city: 'Iqaluit', + }, + { + country_short: 'CA', + region: 'Northwest Territories', + city: 'Yellowknife', + }, + { + country_short: 'KP', + region: "P'yongyang", + city: 'Pyongyang', + }, + { + country_short: 'AW', + region: 'Aruba', + city: 'Oranjestad', + }, + { + country_short: 'HT', + region: 'Ouest', + city: 'Port-au-Prince', + }, + { + country_short: 'GY', + region: 'Demerara-Mahaica', + city: 'Georgetown', + }, + { + country_short: 'ST', + region: 'Agua Grande', + city: 'Sao Tome', + }, + { + country_short: 'ET', + region: 'Adis Abeba', + city: 'Addis Ababa', + }, + { + country_short: 'ER', + region: 'Al Awsat', + city: 'Asmara', + }, + { + country_short: 'GW', + region: 'Bissau', + city: 'Bissau', + }, + { + country_short: 'PH', + region: 'Guimaras', + city: 'South Valencia', + }, + { + country_short: 'PH', + region: 'Aurora', + city: 'Baler', + }, + { + country_short: 'PH', + region: 'Samar', + city: 'Gandara', + }, + { + country_short: 'PH', + region: 'Siquijor', + city: 'Maria', + }, + { + country_short: 'AF', + region: 'Daykundi', + city: 'Mazar', + }, + { + country_short: 'AF', + region: 'Kandahar', + city: 'Kandahar', + }, + { + country_short: 'AF', + region: 'Nangarhar', + city: 'Jalalabad', + }, + { + country_short: 'AF', + region: 'Balkh', + city: 'Mazar-e Sharif', + }, + { + country_short: 'AF', + region: 'Khost', + city: 'Khost', + }, + { + country_short: 'AF', + region: 'Nimroz', + city: 'Zaranj', + }, + { + country_short: 'MN', + region: 'Govi-Altay', + city: 'Bayangol', + }, + { + country_short: 'MN', + region: 'Orhon', + city: 'Erdenet', + }, + { + country_short: 'AF', + region: 'Faryab', + city: 'Maymana', + }, + { + country_short: 'AF', + region: 'Laghman', + city: 'Mehtar Lam', + }, + { + country_short: 'BN', + region: 'Belait', + city: 'Kuala Belait', + }, + { + country_short: 'BN', + region: 'Tutong', + city: 'Tutong', + }, + { + country_short: 'SL', + region: 'Eastern', + city: 'Daru', + }, + { + country_short: 'TW', + region: 'Lienchiang', + city: 'Nangan', + }, + { + country_short: 'LK', + region: 'Uva Province', + city: 'Haputale', + }, + { + country_short: 'LK', + region: 'Northern Province', + city: 'Jaffna', + }, + { + country_short: 'LK', + region: 'Sabaragamuwa Province', + city: 'Ratnapura', + }, + { + country_short: 'LV', + region: 'Aluksnes novads', + city: 'Aluksne', + }, + { + country_short: 'LV', + region: 'Ludzas novads', + city: 'Ludza', + }, + { + country_short: 'EE', + region: 'Vorumaa', + city: 'Voru', + }, + { + country_short: 'EE', + region: 'Laane-Virumaa', + city: 'Tapa', + }, + { + country_short: 'MC', + region: 'Monte-Carlo', + city: 'Monte-Carlo', + }, + { + country_short: 'SI', + region: 'Rogaska Slatina', + city: 'Rogaska Slatina', + }, + { + country_short: 'SI', + region: 'Hrastnik', + city: 'Hrastnik', + }, + { + country_short: 'SI', + region: 'Velike Lasce', + city: 'Laze', + }, + { + country_short: 'LV', + region: 'Limbazu novads', + city: 'Limbazi', + }, + { + country_short: 'TJ', + region: 'Khatlon', + city: 'Kuybishevsk', + }, + { + country_short: 'MK', + region: 'Petrovec', + city: 'Sredno Konjari', + }, + { + country_short: 'MK', + region: 'Ilinden', + city: 'Kadino', + }, + { + country_short: 'MK', + region: 'Vinica', + city: 'Vinica', + }, + { + country_short: 'MK', + region: 'Makedonski Brod', + city: 'Makedonski Brod', + }, + { + country_short: 'MK', + region: 'Negotino', + city: 'Negotino', + }, + { + country_short: 'MX', + region: 'Colima', + city: 'Madrid', + }, + { + country_short: 'MT', + region: 'Pembroke', + city: 'Sprachcaffe', + }, + { + country_short: 'RS', + region: 'Kolubarski okrug', + city: 'Osecina', + }, + { + country_short: 'UZ', + region: 'Xorazm', + city: 'Urganch', + }, + { + country_short: 'AZ', + region: 'Samkir', + city: 'Shamkhor', + }, + { + country_short: 'AZ', + region: 'Agdas', + city: 'Agdas', + }, + { + country_short: 'AZ', + region: 'Sirvan', + city: 'Sirvan', + }, + { + country_short: 'AZ', + region: 'Lankaran', + city: 'Lankaran', + }, + { + country_short: 'AZ', + region: 'Imisli', + city: 'Imishli', + }, + { + country_short: 'MD', + region: 'Soldanesti', + city: 'Soldanesti', + }, + { + country_short: 'RS', + region: 'Srednjebanatski okrug', + city: 'Ecka', + }, + { + country_short: 'LY', + region: 'An Nuqat al Khams', + city: 'Zuwarah', + }, + { + country_short: 'TR', + region: 'Tunceli', + city: 'Isitma', + }, + { + country_short: 'GU', + region: 'Mangilao', + city: 'Mangilao Village', + }, + { + country_short: 'JM', + region: 'Saint Catherine', + city: 'Old Harbour Bay', + }, + { + country_short: 'BB', + region: 'Saint Thomas', + city: 'Lears', + }, + { + country_short: 'JM', + region: 'Trelawny', + city: 'Wakefield', + }, + { + country_short: 'JM', + region: 'Portland', + city: 'Port Antonio', + }, + { + country_short: 'AG', + region: 'Saint George', + city: 'Cassada Gardens', + }, + { + country_short: 'JM', + region: 'Hanover', + city: 'Lucea', + }, + { + country_short: 'JM', + region: 'Saint Mary', + city: 'Richmond', + }, + { + country_short: 'JM', + region: 'Saint Andrew', + city: 'Stony Hill', + }, + { + country_short: 'JM', + region: 'Clarendon', + city: 'Chapelton', + }, + { + country_short: 'JM', + region: 'Saint Ann', + city: 'Ocho Rios', + }, + { + country_short: 'JM', + region: 'Westmoreland', + city: 'Negril', + }, + { + country_short: 'YE', + region: 'Lahij', + city: 'Al Milah', + }, + { + country_short: 'HN', + region: 'Olancho', + city: 'Juticalpa', + }, + { + country_short: 'BB', + region: 'Saint Philip', + city: 'Saint Martins', + }, + { + country_short: 'TT', + region: 'Tunapuna-Piarco', + city: 'Arouca', + }, + { + country_short: 'DO', + region: 'Azua', + city: 'Azua', + }, + { + country_short: 'DO', + region: 'San Juan', + city: 'San Pedro', + }, + { + country_short: 'DO', + region: 'Baoruco', + city: 'El Palmar', + }, + { + country_short: 'DO', + region: 'Hato Mayor', + city: 'El Valle', + }, + { + country_short: 'DO', + region: 'Duarte', + city: 'San Francisco de Macoris', + }, + { + country_short: 'DO', + region: 'Monte Cristi', + city: 'Villa Elisa', + }, + { + country_short: 'BV', + region: 'Bouvet Island', + city: 'Bouvet Island', + }, + { + country_short: 'PW', + region: 'Koror', + city: 'Koror', + }, + { + country_short: 'PE', + region: 'Ancash', + city: 'Chimbote', + }, + { + country_short: 'BB', + region: 'Christ Church', + city: 'Oistins', + }, + { + country_short: 'BM', + region: 'Saint George', + city: 'Saint George', + }, + { + country_short: 'BS', + region: 'East Grand Bahama', + city: 'High Rock', + }, + { + country_short: 'BB', + region: 'Saint George', + city: 'Dash Valley', + }, + { + country_short: 'MM', + region: 'Mon', + city: 'Kyaikkami', + }, + { + country_short: 'MM', + region: 'Kayin', + city: 'Hpa-An', + }, + { + country_short: 'BB', + region: 'Saint James', + city: 'Holetown', + }, + { + country_short: 'GD', + region: 'Saint Andrew', + city: 'Grenville', + }, + { + country_short: 'GD', + region: 'Southern Grenadine Islands', + city: 'Hillsborough', + }, + { + country_short: 'GD', + region: 'Saint David', + city: "Saint David's", + }, + { + country_short: 'VC', + region: 'Grenadines', + city: 'Charlestown', + }, + { + country_short: 'BB', + region: 'Saint Lucy', + city: 'Benthams', + }, + { + country_short: 'BB', + region: 'Saint Andrew', + city: 'Shorey', + }, + { + country_short: 'GD', + region: 'Saint John', + city: 'Gouyave', + }, + { + country_short: 'GD', + region: 'Saint Mark', + city: 'Victoria', + }, + { + country_short: 'LC', + region: 'Soufriere', + city: 'Soufriere', + }, + { + country_short: 'BS', + region: 'Black Point', + city: 'Black Point', + }, + { + country_short: 'JM', + region: 'Manchester', + city: 'Mandeville', + }, + { + country_short: 'BS', + region: 'Long Island', + city: 'Clarence Town', + }, + { + country_short: 'DO', + region: 'Barahona', + city: 'Santa Cruz de Barahona', + }, + { + country_short: 'DO', + region: 'Independencia', + city: 'Cristobal', + }, + { + country_short: 'JM', + region: 'Saint Elizabeth', + city: 'Nain', + }, + { + country_short: 'MT', + region: 'Nadur', + city: 'Nadur', + }, + { + country_short: 'MT', + region: 'Hamrun', + city: 'Hamrun', + }, + { + country_short: 'MT', + region: 'Safi', + city: 'Safi', + }, + { + country_short: 'DM', + region: 'Saint Paul', + city: 'Mahaut', + }, + { + country_short: 'DM', + region: 'Saint Peter', + city: 'Bioche', + }, + { + country_short: 'MS', + region: 'Saint Peter', + city: 'Brades', + }, + { + country_short: 'AG', + region: 'Saint Mary', + city: 'Old Road', + }, + { + country_short: 'LC', + region: 'Vieux Fort', + city: 'Vieux Fort', + }, + { + country_short: 'AG', + region: 'Saint Philip', + city: 'Willoughby', + }, + { + country_short: 'DM', + region: 'Saint Luke', + city: 'Pointe Michel', + }, + { + country_short: 'AG', + region: 'Saint Peter', + city: 'Parham', + }, + { + country_short: 'KN', + region: 'Saint Mary Cayon', + city: 'Dalzells', + }, + { + country_short: 'SZ', + region: 'Lubombo', + city: 'Siteki', + }, + { + country_short: 'VC', + region: 'Saint Patrick', + city: 'Rutland Vale', + }, + { + country_short: 'VC', + region: 'Charlotte', + city: 'Georgetown', + }, + { + country_short: 'BB', + region: 'Saint Peter', + city: 'Speightstown', + }, + { + country_short: 'LC', + region: 'Choiseul', + city: 'La Fargue', + }, + { + country_short: 'DM', + region: 'Saint Patrick', + city: 'La Plaine', + }, + { + country_short: 'DM', + region: 'Saint Andrew', + city: 'Marigot', + }, + { + country_short: 'MV', + region: 'South Thiladhunmathi', + city: 'Kulhudhuffushi', + }, + { + country_short: 'MV', + region: 'North Maalhosmadulu', + city: 'Ugoofaaru', + }, + { + country_short: 'NI', + region: 'Granada', + city: 'Granada', + }, + { + country_short: 'JM', + region: 'Saint Thomas', + city: 'Easington', + }, + { + country_short: 'GD', + region: 'Saint Patrick', + city: 'Sauteurs', + }, + { + country_short: 'AF', + region: 'Bamyan', + city: 'Panjab', + }, + { + country_short: 'KN', + region: 'Saint Thomas Middle Island', + city: 'Half Way Tree', + }, + { + country_short: 'KN', + region: 'Christ Church Nichola Town', + city: 'Mansion', + }, + { + country_short: 'AG', + region: 'Redonda', + city: 'Redonda', + }, + { + country_short: 'MK', + region: 'Kocani', + city: 'Kochani', + }, + { + country_short: 'MK', + region: 'Jegunovce', + city: 'Vratnica', + }, + { + country_short: 'MK', + region: 'Makedonska Kamenica', + city: 'Makedonska Kamenica', + }, + { + country_short: 'MK', + region: 'Lozovo', + city: 'Lozovo', + }, + { + country_short: 'MK', + region: 'Caska', + city: 'Bogomila', + }, + { + country_short: 'MK', + region: 'Rosoman', + city: 'Rosoman', + }, + { + country_short: 'MK', + region: 'Gradsko', + city: 'Gradsko', + }, + { + country_short: 'MK', + region: 'Dojran', + city: 'Star Dojran', + }, + { + country_short: 'MK', + region: 'Vevcani', + city: 'Oktisi', + }, + { + country_short: 'MK', + region: 'Kriva Palanka', + city: 'Kriva Palanka', + }, + { + country_short: 'MK', + region: 'Karbinci', + city: 'Karbinci', + }, + { + country_short: 'MK', + region: 'Zelenikovo', + city: 'Zelenikovo', + }, + { + country_short: 'SI', + region: 'Vojnik', + city: 'Vojnik', + }, + { + country_short: 'SI', + region: 'Lasko', + city: 'Lasko', + }, + { + country_short: 'SI', + region: 'Prevalje', + city: 'Prevalje', + }, + { + country_short: 'SI', + region: 'Sentjur', + city: 'Sentjur', + }, + { + country_short: 'SI', + region: 'Podcetrtek', + city: 'Pristava pri Mestinju', + }, + { + country_short: 'SI', + region: 'Recica ob Savinji', + city: 'Spodnja Recica', + }, + { + country_short: 'SI', + region: 'Dobrna', + city: 'Dobrna', + }, + { + country_short: 'SI', + region: 'Mozirje', + city: 'Mozirje', + }, + { + country_short: 'SI', + region: 'Braslovce', + city: 'Kamence', + }, + { + country_short: 'SI', + region: 'Smartno ob Paki', + city: 'Smartno ob Paki', + }, + { + country_short: 'LB', + region: 'Liban-Sud', + city: 'Sidon', + }, + { + country_short: 'RS', + region: 'Branicevski okrug', + city: 'Milanovac', + }, + { + country_short: 'SS', + region: 'Northern Bahr el Ghazal', + city: 'Aweil', + }, + { + country_short: 'TF', + region: 'French Southern Territories', + city: 'Port-aux-Francais', + }, + { + country_short: 'BF', + region: 'Tuy', + city: 'Hounde', + }, + { + country_short: 'CD', + region: 'Nord-Kivu', + city: 'Beni', + }, + { + country_short: 'GN', + region: 'Siguiri', + city: 'Siguiri', + }, + { + country_short: 'TM', + region: 'Balkan', + city: 'Turkmenistan', + }, + { + country_short: 'CF', + region: 'Bamingui-Bangoran', + city: 'Ndele', + }, + { + country_short: 'CF', + region: 'Nana-Mambere', + city: 'Bouar', + }, + { + country_short: 'CF', + region: 'Ouham', + city: 'Bouca', + }, + { + country_short: 'CF', + region: 'Ouham-Pende', + city: 'Paoua', + }, + { + country_short: 'CF', + region: 'Gribingui', + city: 'Kaga Bandoro', + }, + { + country_short: 'CF', + region: 'Kemo-Gribingui', + city: 'Dekoa', + }, + { + country_short: 'NE', + region: 'Diffa', + city: 'Diffa', + }, + { + country_short: 'NE', + region: 'Tillaberi', + city: 'Tillaberi', + }, + { + country_short: 'LR', + region: 'Grand Gedeh', + city: 'Zwedru', + }, + { + country_short: 'LR', + region: 'Sinoe', + city: 'Greenville', + }, + { + country_short: 'SS', + region: 'Eastern Equatoria', + city: 'Torit', + }, + { + country_short: 'SS', + region: 'Western Equatoria', + city: 'Yambio', + }, + { + country_short: 'ML', + region: 'Tombouctou', + city: 'Niafunke', + }, + { + country_short: 'ML', + region: 'Gao', + city: 'Ansongo', + }, + { + country_short: 'BF', + region: 'Tapoa', + city: 'Diapaga', + }, + { + country_short: 'ML', + region: 'Kidal', + city: 'Kidal', + }, + { + country_short: 'PG', + region: 'Southern Highlands', + city: 'Mendi', + }, + { + country_short: 'MT', + region: 'Xaghra', + city: 'Xaghra', + }, + { + country_short: 'MT', + region: 'Munxar', + city: 'Xlendi', + }, + { + country_short: 'MT', + region: 'Fontana', + city: 'Manresa', + }, + { + country_short: 'MT', + region: 'Ghasri', + city: 'Ghasri', + }, + { + country_short: 'MT', + region: 'Qala', + city: 'Qala', + }, + { + country_short: 'MT', + region: 'Xewkija', + city: 'Xewkija', + }, + { + country_short: 'BO', + region: 'Oruro', + city: 'Challapata', + }, + { + country_short: 'RU', + region: 'Ingushetiya, Respublika', + city: 'Ingushetiya, Respublika', + }, + { + country_short: 'SI', + region: 'Ribnica', + city: 'Ribnica', + }, + { + country_short: 'SI', + region: 'Sezana', + city: 'Sezana', + }, + { + country_short: 'SI', + region: 'Moravce', + city: 'Negastrn', + }, + { + country_short: 'SI', + region: 'Metlika', + city: 'Metlika', + }, + { + country_short: 'SI', + region: 'Lukovica', + city: 'Kompolje', + }, + { + country_short: 'SI', + region: 'Mezica', + city: 'Mezica', + }, + { + country_short: 'SI', + region: 'Apace', + city: 'Sladki Vrh', + }, + { + country_short: 'LV', + region: 'Saulkrastu novads', + city: 'Zvejniekciems', + }, + { + country_short: 'LV', + region: 'Balvu novads', + city: 'Balvi', + }, + { + country_short: 'AL', + region: 'Berat', + city: 'Berat', + }, + { + country_short: 'AL', + region: 'Shkoder', + city: 'Shkoder', + }, + { + country_short: 'MT', + region: 'Gharb', + city: 'Birbuba', + }, + { + country_short: 'MT', + region: 'Kercem', + city: 'Kercem', + }, + { + country_short: 'MT', + region: 'Kalkara', + city: 'Kalkara', + }, + { + country_short: 'EE', + region: 'Jarvamaa', + city: 'Koeru', + }, + { + country_short: 'EE', + region: 'Raplamaa', + city: 'Rapla', + }, + { + country_short: 'LV', + region: 'Kraslavas novads', + city: 'Kraslava', + }, + { + country_short: 'RU', + region: 'Nenetskiy avtonomnyy okrug', + city: "Nar'yan-Mar", + }, + { + country_short: 'LV', + region: 'Livanu novads', + city: 'Silavas', + }, + { + country_short: 'MT', + region: 'Gudja', + city: 'Gudja', + }, + { + country_short: 'MT', + region: 'Ghajnsielem', + city: 'Mgarr', + }, + { + country_short: 'LU', + region: 'Diekirch', + city: 'Diekirch', + }, + { + country_short: 'LU', + region: 'Redange', + city: 'Redange-sur-Attert', + }, + { + country_short: 'LU', + region: 'Capellen', + city: 'Koerich', + }, + { + country_short: 'SI', + region: 'Sentjernej', + city: 'Groblje pri Prekopi', + }, + { + country_short: 'SI', + region: 'Trzic', + city: 'Trzic', + }, + { + country_short: 'ME', + region: 'Andrijevica', + city: 'Andrijevica', + }, + { + country_short: 'MK', + region: 'Cesinovo-Oblesevo', + city: 'Obleshevo', + }, + { + country_short: 'TR', + region: 'Igdir', + city: 'Igdir', + }, + { + country_short: 'AL', + region: 'Gjirokaster', + city: 'Gjirokaster', + }, + { + country_short: 'AL', + region: 'Kukes', + city: 'Kukes', + }, + { + country_short: 'AL', + region: 'Korce', + city: 'Korce', + }, + { + country_short: 'MK', + region: 'Rankovce', + city: 'Rankovce', + }, + { + country_short: 'MK', + region: 'Bogdanci', + city: 'Bogdanci', + }, + { + country_short: 'MK', + region: 'Plasnica', + city: 'Lisicani', + }, + { + country_short: 'AZ', + region: 'Qax', + city: 'Qax', + }, + { + country_short: 'AZ', + region: 'Kurdamir', + city: 'Ucarli', + }, + { + country_short: 'AZ', + region: 'Tartar', + city: 'Terter', + }, + { + country_short: 'AZ', + region: 'Yevlax', + city: 'Yevlakh', + }, + { + country_short: 'AZ', + region: 'Naftalan', + city: 'Naftalan', + }, + { + country_short: 'AZ', + region: 'Ismayilli', + city: 'Basqal', + }, + { + country_short: 'AZ', + region: 'Xizi', + city: 'Altiagac', + }, + { + country_short: 'LI', + region: 'Triesenberg', + city: 'Triesenberg', + }, + { + country_short: 'GE', + region: "Rach'a-Lechkhumi-Kvemo Svaneti", + city: 'Oni', + }, + { + country_short: 'MC', + region: 'La Condamine', + city: 'La Condamine', + }, + { + country_short: 'MC', + region: 'Saint-Roman', + city: 'Saint-Roman', + }, + { + country_short: 'LV', + region: 'Valkas novads', + city: 'Valka', + }, + { + country_short: 'MT', + region: 'Xghajra', + city: 'Xghajra', + }, + { + country_short: 'SS', + region: 'Upper Nile', + city: 'Malakal', + }, + { + country_short: 'IS', + region: 'Vesturland', + city: 'Borgarnes', + }, + { + country_short: 'FO', + region: 'Suduroy', + city: 'Vagur', + }, + { + country_short: 'AZ', + region: 'Oguz', + city: 'Oguz', + }, + { + country_short: 'FO', + region: 'Nordoyar', + city: 'Klaksvik', + }, + { + country_short: 'GR', + region: 'Agion Oros', + city: 'Karyes', + }, + { + country_short: 'YE', + region: 'Al Jawf', + city: 'Al Hazm', + }, + { + country_short: 'IS', + region: 'Vestfirdir', + city: 'Isafjoerdur', + }, + { + country_short: 'SI', + region: 'Hoce-Slivnica', + city: 'Rogoza', + }, + { + country_short: 'SI', + region: 'Videm', + city: 'Sela', + }, + { + country_short: 'SI', + region: 'Slovenska Bistrica', + city: 'Slovenska Bistrica', + }, + { + country_short: 'SI', + region: 'Hajdina', + city: 'Spodnja Hajdina', + }, + { + country_short: 'SI', + region: 'Preddvor', + city: 'Preddvor', + }, + { + country_short: 'SI', + region: 'Menges', + city: 'Menges', + }, + { + country_short: 'SI', + region: 'Poljcane', + city: 'Poljcane', + }, + { + country_short: 'SI', + region: 'Medvode', + city: 'Medvode', + }, + { + country_short: 'AX', + region: 'Finstroem', + city: 'Finstrom', + }, + { + country_short: 'AX', + region: 'Sund', + city: 'Sund', + }, + { + country_short: 'MK', + region: 'Lipkovo', + city: 'Otlja', + }, + { + country_short: 'UZ', + region: 'Andijon', + city: 'Andijon', + }, + { + country_short: 'UZ', + region: 'Navoiy', + city: 'Tomdibuloq', + }, + { + country_short: 'LU', + region: 'Clervaux', + city: 'Clervaux', + }, + { + country_short: 'LU', + region: 'Echternach', + city: 'Echternach', + }, + { + country_short: 'LU', + region: 'Remich', + city: 'Remich', + }, + { + country_short: 'EE', + region: 'Saaremaa', + city: 'Kuressaare', + }, + { + country_short: 'EE', + region: 'Valgamaa', + city: 'Valga', + }, + { + country_short: 'EE', + region: 'Polvamaa', + city: 'Polva', + }, + { + country_short: 'TD', + region: 'Ouaddai', + city: 'Abeche', + }, + { + country_short: 'GN', + region: 'Kankan', + city: 'Kankan', + }, + { + country_short: 'BN', + region: 'Temburong', + city: 'Bangar', + }, + { + country_short: 'SI', + region: 'Gornja Radgona', + city: 'Gornja Radgona', + }, + { + country_short: 'SI', + region: 'Cerknica', + city: 'Cerknica', + }, + { + country_short: 'SI', + region: 'Loska dolina', + city: 'Klance', + }, + { + country_short: 'SI', + region: 'Vransko', + city: 'Vransko', + }, + { + country_short: 'SI', + region: 'Sencur', + city: 'Sencur', + }, + { + country_short: 'UZ', + region: 'Surxondaryo', + city: 'Tirmiz', + }, + { + country_short: 'UZ', + region: 'Namangan', + city: 'Turagurghon', + }, + { + country_short: 'UZ', + region: "Qoraqalpog'iston Respublikasi", + city: "Mang'it Shahri", + }, + { + country_short: 'CG', + region: 'Cuvette', + city: 'Owando', + }, + { + country_short: 'MT', + region: 'Iklin', + city: 'L-Iklin', + }, + { + country_short: 'SI', + region: 'Lovrenc na Pohorju', + city: 'Lovrenc na Pohorju', + }, + { + country_short: 'SI', + region: 'Ivancna Gorica', + city: 'Vir pri Sticni', + }, + { + country_short: 'SI', + region: 'Idrija', + city: 'Idrija', + }, + { + country_short: 'TJ', + region: 'Kuhistoni Badakhshon', + city: 'Khorugh', + }, + { + country_short: 'SI', + region: 'Zelezniki', + city: 'Zelezniki', + }, + { + country_short: 'LU', + region: 'Vianden', + city: 'Tandel', + }, + { + country_short: 'AD', + region: 'Sant Julia de Loria', + city: 'Sant Julia de Loria', + }, + { + country_short: 'AD', + region: 'Ordino', + city: 'Ordino', + }, + { + country_short: 'AZ', + region: 'Zardab', + city: 'Zardob', + }, + { + country_short: 'AZ', + region: 'Balakan', + city: 'Belokany', + }, + { + country_short: 'AZ', + region: 'Samaxi', + city: 'Shamakhi', + }, + { + country_short: 'AZ', + region: 'Qabala', + city: 'Qutqashen', + }, + { + country_short: 'AZ', + region: 'Neftcala', + city: 'Neftcala', + }, + { + country_short: 'MT', + region: 'Kirkop', + city: 'Kirkop', + }, + { + country_short: 'SI', + region: 'Race-Fram', + city: 'Race', + }, + { + country_short: 'SI', + region: 'Makole', + city: 'Loznica', + }, + { + country_short: 'SI', + region: 'Razkrizje', + city: 'Gibina', + }, + { + country_short: 'SI', + region: 'Miklavz na Dravskem polju', + city: 'Miklavz na Dravskem Polju', + }, + { + country_short: 'SI', + region: 'Lenart', + city: 'Lenart v Slovenskih Goricah', + }, + { + country_short: 'LV', + region: 'Varaklanu novads', + city: 'Varaklani', + }, + { + country_short: 'MD', + region: 'Falesti', + city: 'Falesti', + }, + { + country_short: 'GL', + region: 'Kommune Kujalleq', + city: 'Qaqortoq', + }, + { + country_short: 'GL', + region: 'Kommune Qeqertalik', + city: 'Qasigiannguit', + }, + { + country_short: 'FO', + region: 'Vagar', + city: 'Sorvagur', + }, + { + country_short: 'MK', + region: 'Novaci', + city: 'Novaci', + }, + { + country_short: 'SJ', + region: 'Svalbard and Jan Mayen', + city: 'Longyearbyen', + }, + { + country_short: 'SI', + region: 'Komen', + city: 'Stanjel', + }, + { + country_short: 'SI', + region: 'Starse', + city: 'Loka', + }, + { + country_short: 'SI', + region: 'Tabor', + city: 'Kapla', + }, + { + country_short: 'SI', + region: 'Benedikt', + city: 'Benedikt', + }, + { + country_short: 'SI', + region: 'Verzej', + city: 'Verzej', + }, + { + country_short: 'SI', + region: 'Jursinci', + city: 'Jursinci', + }, + { + country_short: 'SI', + region: 'Borovnica', + city: 'Borovnica', + }, + { + country_short: 'SI', + region: 'Bovec', + city: 'Bovec', + }, + { + country_short: 'SI', + region: 'Oplotnica', + city: 'Oplotnica', + }, + { + country_short: 'SI', + region: 'Velika Polana', + city: 'Velika Polana', + }, + { + country_short: 'SI', + region: 'Dobje', + city: 'Dobje pri Planini', + }, + { + country_short: 'SI', + region: 'Straza', + city: 'Straza', + }, + { + country_short: 'SI', + region: 'Slovenske Konjice', + city: 'Slovenske Konjice', + }, + { + country_short: 'MT', + region: 'Qrendi', + city: 'Qrendi', + }, + { + country_short: 'MT', + region: 'Marsaxlokk', + city: 'Marsaxlokk', + }, + { + country_short: 'MT', + region: 'Birgu', + city: 'Vittoriosa', + }, + { + country_short: 'MC', + region: 'Fontvieille', + city: 'Fontvieille', + }, + { + country_short: 'MC', + region: 'Moneghetti', + city: 'Moneghetti', + }, + { + country_short: 'MD', + region: 'Donduseni', + city: 'Donduseni', + }, + { + country_short: 'LB', + region: 'Baalbek-Hermel', + city: 'Baalbek', + }, + { + country_short: 'SI', + region: 'Nazarje', + city: 'Brdo', + }, + { + country_short: 'SI', + region: 'Bloke', + city: 'Sveta Trojica', + }, + { + country_short: 'SI', + region: 'Komenda', + city: 'Moste', + }, + { + country_short: 'SI', + region: 'Ig', + city: 'Ig', + }, + { + country_short: 'SI', + region: 'Radece', + city: 'Radece', + }, + { + country_short: 'SI', + region: 'Ravne na Koroskem', + city: 'Kotlje', + }, + { + country_short: 'SI', + region: 'Miren-Kostanjevica', + city: 'Miren', + }, + { + country_short: 'SI', + region: 'Log-Dragomer', + city: 'Dragomer', + }, + { + country_short: 'SI', + region: 'Dravograd', + city: 'Dravograd', + }, + { + country_short: 'SI', + region: 'Zetale', + city: 'Cermozise', + }, + { + country_short: 'KG', + region: 'Batken', + city: 'Batken', + }, + { + country_short: 'LI', + region: 'Planken', + city: 'Planken', + }, + { + country_short: 'LV', + region: 'Augsdaugavas novads', + city: 'Ilukste', + }, + { + country_short: 'SI', + region: 'Salovci', + city: 'Lucova', + }, + { + country_short: 'AZ', + region: 'Goycay', + city: 'Geoktschai', + }, + { + country_short: 'AZ', + region: 'Goygol', + city: 'Goygol', + }, + { + country_short: 'AZ', + region: 'Beylaqan', + city: 'Orconikidze', + }, + { + country_short: 'AZ', + region: 'Ucar', + city: 'Ujar', + }, + { + country_short: 'AZ', + region: 'Sabirabad', + city: 'Sabirabad', + }, + { + country_short: 'AZ', + region: 'Barda', + city: 'Barda', + }, + { + country_short: 'SM', + region: 'Chiesanuova', + city: 'Chiesanuova', + }, + { + country_short: 'SI', + region: 'Zavrc', + city: 'Turski Vrh', + }, + { + country_short: 'AZ', + region: 'Zaqatala', + city: 'Zaqatala', + }, + { + country_short: 'MK', + region: 'Sopiste', + city: 'Sopishte', + }, + { + country_short: 'SI', + region: 'Vitanje', + city: 'Rakovec', + }, + { + country_short: 'YT', + region: 'Pamandzi', + city: 'Pamandzi', + }, + { + country_short: 'YT', + region: 'Bandrele', + city: 'Bandrele', + }, + { + country_short: 'YT', + region: 'Sada', + city: 'Sada', + }, + { + country_short: 'YT', + region: 'Kani-Keli', + city: 'Kani Keli', + }, + { + country_short: 'YT', + region: 'Ouangani', + city: 'Ouangani', + }, + { + country_short: 'YT', + region: 'Bandraboua', + city: 'Bandraboua', + }, + { + country_short: 'SI', + region: 'Zuzemberk', + city: 'Zuzemberk', + }, + { + country_short: 'SI', + region: 'Ankaran', + city: 'Ankaran', + }, + { + country_short: 'SI', + region: 'Kostel', + city: 'Planina', + }, + { + country_short: 'MT', + region: 'Sannat', + city: 'Sannat', + }, + { + country_short: 'JO', + region: 'At Tafilah', + city: 'Busayra', + }, + { + country_short: 'SI', + region: 'Ruse', + city: 'Ruse', + }, + { + country_short: 'AZ', + region: 'Fuzuli', + city: 'Horadiz', + }, + { + country_short: 'AZ', + region: 'Samux', + city: 'Qarayeri', + }, + { + country_short: 'YE', + region: 'Al Mahrah', + city: 'Sayhut', + }, + { + country_short: 'TM', + region: 'Mary', + city: 'Mary', + }, + { + country_short: 'CR', + region: 'Puntarenas', + city: 'Ciudad Cortes', + }, + { + country_short: 'SI', + region: 'Turnisce', + city: 'Turnisce', + }, + { + country_short: 'SI', + region: 'Loski Potok', + city: 'Srednja Vas-Loski Potok', + }, + { + country_short: 'TM', + region: 'Lebap', + city: 'Turkmenabat', + }, + { + country_short: 'TM', + region: 'Dasoguz', + city: 'Koeneuergench', + }, + { + country_short: 'MK', + region: 'Demir Kapija', + city: 'Demir Kapija', + }, + { + country_short: 'SI', + region: 'Sodrazica', + city: 'Sodrazica', + }, + { + country_short: 'SI', + region: 'Sveti Jurij ob Scavnici', + city: 'Videm', + }, + { + country_short: 'MK', + region: 'Demir Hisar', + city: 'Sopotnica', + }, + { + country_short: 'LK', + region: 'North Central Province', + city: 'Anuradhapura', + }, + { + country_short: 'GU', + region: 'Yigo', + city: 'Yigo Village', + }, + { + country_short: 'GU', + region: 'Sinajana', + city: 'Sinajana Village', + }, + { + country_short: 'GU', + region: 'Santa Rita', + city: 'Santa Rita Village', + }, + { + country_short: 'GU', + region: 'Agana Heights', + city: 'Agana Heights Municipality', + }, + { + country_short: 'GU', + region: 'Agat', + city: 'Agat Village', + }, + { + country_short: 'KE', + region: 'Vihiga', + city: 'Luanda', + }, + { + country_short: 'UG', + region: 'Kalungu', + city: 'Bukulula', + }, + { + country_short: 'MW', + region: 'Dedza', + city: 'Dedza', + }, + { + country_short: 'MW', + region: 'Neno', + city: 'Neno', + }, + { + country_short: 'UG', + region: 'Kyegegwa', + city: 'Kyegegwa', + }, + { + country_short: 'UG', + region: 'Nwoya', + city: 'Kacabu', + }, + { + country_short: 'UG', + region: 'Kaliro', + city: 'Kaliro', + }, + { + country_short: 'UG', + region: 'Kiryandongo', + city: 'Kiryandongo', + }, + { + country_short: 'UG', + region: 'Kaberamaido', + city: 'Kaberamaido', + }, + { + country_short: 'UG', + region: 'Rakai', + city: 'Rakai', + }, + { + country_short: 'UG', + region: 'Bugiri', + city: 'Bugiri', + }, + { + country_short: 'UG', + region: 'Nakaseke', + city: 'Nakaseke', + }, + { + country_short: 'UG', + region: 'Kasese', + city: 'Kilembe', + }, + { + country_short: 'UG', + region: 'Kayunga', + city: 'Kayunga', + }, + { + country_short: 'UG', + region: 'Buhweju', + city: 'Rugongo', + }, + { + country_short: 'UG', + region: 'Mbale', + city: 'Mbale', + }, + { + country_short: 'UG', + region: 'Mityana', + city: 'Mityana', + }, + { + country_short: 'UG', + region: 'Kalangala', + city: 'Kalangala', + }, + { + country_short: 'UG', + region: 'Bududa', + city: 'Bududa', + }, + { + country_short: 'UG', + region: 'Yumbe', + city: 'Yumbe', + }, + { + country_short: 'UG', + region: 'Kween', + city: 'Binyin', + }, + { + country_short: 'UG', + region: 'Sheema', + city: 'Kibingo', + }, + { + country_short: 'UG', + region: 'Kamuli', + city: 'Kamuli', + }, + { + country_short: 'UG', + region: 'Manafwa', + city: 'Manafwa', + }, + { + country_short: 'UG', + region: 'Buikwe', + city: 'Njeru', + }, + { + country_short: 'UG', + region: 'Kiboga', + city: 'Kiboga', + }, + { + country_short: 'UG', + region: 'Soroti', + city: 'Soroti', + }, + { + country_short: 'UG', + region: 'Mpigi', + city: 'Mpigi', + }, + { + country_short: 'UG', + region: 'Gomba', + city: 'Kanoni', + }, + { + country_short: 'UG', + region: 'Isingiro', + city: 'Isingiro', + }, + { + country_short: 'UG', + region: 'Napak', + city: 'Napak', + }, + { + country_short: 'UG', + region: 'Abim', + city: 'Abim', + }, + { + country_short: 'UG', + region: 'Bushenyi', + city: 'Bushenyi', + }, + { + country_short: 'UG', + region: 'Bukedea', + city: 'Bukedea', + }, + { + country_short: 'UG', + region: 'Ntungamo', + city: 'Ntungamo', + }, + { + country_short: 'UG', + region: 'Masindi', + city: 'Masindi', + }, + { + country_short: 'UG', + region: 'Mayuge', + city: 'Mayuge', + }, + { + country_short: 'UG', + region: 'Moroto', + city: 'Moroto', + }, + { + country_short: 'UG', + region: 'Lyantonde', + city: 'Lyantonde', + }, + { + country_short: 'UG', + region: 'Bulambuli', + city: 'Bulambuli', + }, + { + country_short: 'UG', + region: 'Sironko', + city: 'Sironko', + }, + { + country_short: 'UG', + region: 'Lira', + city: 'Lira', + }, + { + country_short: 'UG', + region: 'Apac', + city: 'Apac', + }, + { + country_short: 'UG', + region: 'Luwero', + city: 'Wobulenzi', + }, + { + country_short: 'UG', + region: 'Amolatar', + city: 'Amolatar', + }, + { + country_short: 'UG', + region: 'Kamwenge', + city: 'Kamwenge', + }, + { + country_short: 'UG', + region: 'Lwengo', + city: 'Lwengo', + }, + { + country_short: 'UG', + region: 'Kisoro', + city: 'Kisoro', + }, + { + country_short: 'UG', + region: 'Tororo', + city: 'Tororo', + }, + { + country_short: 'UG', + region: 'Luuka', + city: 'Luuka Town', + }, + { + country_short: 'UG', + region: 'Sembabule', + city: 'Sembabule', + }, + { + country_short: 'UG', + region: 'Kapchorwa', + city: 'Kapchorwa', + }, + { + country_short: 'UG', + region: 'Kole', + city: 'Kole', + }, + { + country_short: 'UG', + region: 'Kyankwanzi', + city: 'Kyankwanzi', + }, + { + country_short: 'UG', + region: 'Alebtong', + city: 'Alebtong', + }, + { + country_short: 'UG', + region: 'Kibuku', + city: 'Kibuku', + }, + { + country_short: 'UG', + region: 'Katakwi', + city: 'Katakwi', + }, + { + country_short: 'UG', + region: 'Kaabong', + city: 'Kaabong', + }, + { + country_short: 'UG', + region: 'Kibaale', + city: 'Kagadi', + }, + { + country_short: 'UG', + region: 'Adjumani', + city: 'Adjumani', + }, + { + country_short: 'UG', + region: 'Ntoroko', + city: 'Ntoroko', + }, + { + country_short: 'UG', + region: 'Rubirizi', + city: 'Rubirizi', + }, + { + country_short: 'UG', + region: 'Agago', + city: 'Pader Palwo', + }, + { + country_short: 'UG', + region: 'Nebbi', + city: 'Nebbi', + }, + { + country_short: 'SD', + region: 'White Nile', + city: 'Kosti', + }, + { + country_short: 'SD', + region: 'Central Darfur', + city: 'Zalingei', + }, + { + country_short: 'SO', + region: 'Mudug', + city: 'Gaalkacyo', + }, + { + country_short: 'BI', + region: 'Bururi', + city: 'Bururi', + }, + { + country_short: 'SL', + region: 'Northern', + city: 'Makeni', + }, + { + country_short: 'GQ', + region: 'Kie-Ntem', + city: 'Ncue', + }, + { + country_short: 'GH', + region: 'Volta', + city: 'Hohoe', + }, + { + country_short: 'GN', + region: 'Kouroussa', + city: 'Kouroussa', + }, + { + country_short: 'GN', + region: 'Dinguiraye', + city: 'Dinguiraye', + }, + { + country_short: 'BF', + region: 'Oubritenga', + city: 'Ziniare', + }, + { + country_short: 'BF', + region: 'Zoundweogo', + city: 'Manga', + }, + { + country_short: 'BF', + region: 'Oudalan', + city: 'Gorom-Gorom', + }, + { + country_short: 'BF', + region: 'Passore', + city: 'Yako', + }, + { + country_short: 'BF', + region: 'Comoe', + city: 'Banfora', + }, + { + country_short: 'BF', + region: 'Kouritenga', + city: 'Koupela', + }, + { + country_short: 'BF', + region: 'Leraba', + city: 'Sindou', + }, + { + country_short: 'BF', + region: 'Nahouri', + city: 'Po', + }, + { + country_short: 'BF', + region: 'Seno', + city: 'Dori', + }, + { + country_short: 'BF', + region: 'Bazega', + city: 'Kombissiri', + }, + { + country_short: 'BF', + region: 'Kossi', + city: 'Nouna', + }, + { + country_short: 'BF', + region: 'Namentenga', + city: 'Boulsa', + }, + { + country_short: 'BF', + region: 'Sissili', + city: 'Leo', + }, + { + country_short: 'BF', + region: 'Gourma', + city: "Fada N'Gourma", + }, + { + country_short: 'BF', + region: 'Ganzourgou', + city: 'Zorgho', + }, + { + country_short: 'BF', + region: 'Ziro', + city: 'Sapouy', + }, + { + country_short: 'BF', + region: 'Loroum', + city: 'Titao', + }, + { + country_short: 'BF', + region: 'Kourweogo', + city: 'Bousse', + }, + { + country_short: 'BF', + region: 'Sanguie', + city: 'Reo', + }, + { + country_short: 'BF', + region: 'Kompienga', + city: 'Pama', + }, + { + country_short: 'BF', + region: 'Sourou', + city: 'Tougan', + }, + { + country_short: 'BF', + region: 'Gnagna', + city: 'Bogande', + }, + { + country_short: 'BF', + region: 'Soum', + city: 'Djibo', + }, + { + country_short: 'BF', + region: 'Zondoma', + city: 'Gourcy', + }, + { + country_short: 'SO', + region: 'Jubbada Hoose', + city: 'Kismayo', + }, + { + country_short: 'SO', + region: 'Sool', + city: 'Laascaanood', + }, + { + country_short: 'TD', + region: 'Bahr el Ghazal', + city: 'Salal', + }, + { + country_short: 'SL', + region: 'Southern', + city: 'Mogbwemo', + }, + { + country_short: 'SL', + region: 'North Western', + city: 'Port Loko', + }, + { + country_short: 'CD', + region: 'Kongo Central', + city: 'Tshela', + }, + { + country_short: 'LA', + region: 'Louangphabang', + city: 'Luang Prabang', + }, + { + country_short: 'KH', + region: 'Kampong Spueu', + city: 'Kampong Speu', + }, + { + country_short: 'PG', + region: 'Eastern Highlands', + city: 'Goroka', + }, + { + country_short: 'PG', + region: 'Morobe', + city: 'Lae', + }, + { + country_short: 'PG', + region: 'Western', + city: 'Tabubil', + }, + { + country_short: 'TL', + region: 'Ainaro', + city: 'Xanana Gusm', + }, + { + country_short: 'BT', + region: 'Tsirang', + city: 'Tsirang', + }, + { + country_short: 'BT', + region: 'Punakha', + city: 'Punakha', + }, + { + country_short: 'BT', + region: 'Wangdue Phodrang', + city: 'Lung Chen', + }, + { + country_short: 'BT', + region: 'Samtse', + city: 'Samtse', + }, + { + country_short: 'BT', + region: 'Paro', + city: 'Paro', + }, + { + country_short: 'AF', + region: 'Ghazni', + city: 'Ghazni', + }, + { + country_short: 'NR', + region: 'Anetan', + city: 'Ronave', + }, + { + country_short: 'MV', + region: 'South Huvadhu Atoll', + city: 'Thinadhoo', + }, + { + country_short: 'KI', + region: 'Line Islands', + city: 'Tabwakea Village', + }, + { + country_short: 'MN', + region: 'Dornod', + city: 'Choibalsan', + }, + { + country_short: 'MV', + region: 'North Thiladhunmathi', + city: 'Dhidhdhoo', + }, + { + country_short: 'AS', + region: 'Western District', + city: 'Tafuna', + }, + { + country_short: 'PG', + region: 'East Sepik', + city: 'Wewak', + }, + { + country_short: 'BT', + region: 'Trashigang', + city: 'Kanglung', + }, + { + country_short: 'MN', + region: 'Tov', + city: 'Orgil', + }, + { + country_short: 'WS', + region: "Fa'asaleleaga", + city: 'Vaimauga', + }, + { + country_short: 'AQ', + region: 'Antarctica', + city: 'McMurdo Station', + }, + { + country_short: 'PG', + region: 'Madang', + city: 'Bank', + }, + { + country_short: 'MM', + region: 'Rakhine', + city: 'Sittwe', + }, + { + country_short: 'BT', + region: 'Chhukha', + city: 'Phuntsholing', + }, + { + country_short: 'FM', + region: 'Yap', + city: 'Colonia', + }, + { + country_short: 'BT', + region: 'Samdrup Jongkhar', + city: 'Samdrup Jongkhar', + }, + { + country_short: 'GU', + region: 'Mongmong-Toto-Maite', + city: 'Mongmong-Toto-Maite Village', + }, + { + country_short: 'LK', + region: 'Eastern Province', + city: 'Trincomalee', + }, + { + country_short: 'WF', + region: 'Sigave', + city: 'Alo', + }, + { + country_short: 'TL', + region: 'Liquica', + city: 'Vatunau', + }, + { + country_short: 'TL', + region: 'Cova Lima', + city: 'Orun', + }, + { + country_short: 'LA', + region: 'Phongsali', + city: 'Phongsali', + }, + { + country_short: 'PG', + region: 'Western Highlands', + city: 'Mount Hagen', + }, + { + country_short: 'PF', + region: 'Iles Sous-le-Vent', + city: 'Vaitape', + }, + { + country_short: 'NZ', + region: 'Chatham Islands Territory', + city: 'Waitangi', + }, + { + country_short: 'BQ', + region: ' Sint Eustatius and Saba"', + city: 'Bonaire', + }, + { + country_short: 'CU', + region: 'Santiago de Cuba', + city: 'Santiago de Cuba', + }, + { + country_short: 'CU', + region: 'Holguin', + city: 'Holguin', + }, + { + country_short: 'VE', + region: 'Sucre', + city: 'Cumana', + }, + { + country_short: 'VE', + region: 'Barinas', + city: 'Barinitas', + }, + { + country_short: 'EC', + region: 'Galapagos', + city: 'Puerto Ayora', + }, + { + country_short: 'PE', + region: 'Cusco', + city: 'Cusco', + }, + { + country_short: 'TT', + region: 'Chaguanas', + city: 'Chaguanas', + }, + { + country_short: 'BO', + region: 'Cochabamba', + city: 'Cochabamba', + }, + { + country_short: 'PY', + region: 'Central', + city: 'San Lorenzo', + }, + { + country_short: 'PF', + region: 'Iles Tuamotu-Gambier', + city: 'Rikitea', + }, + { + country_short: 'FM', + region: 'Chuuk', + city: 'Weno', + }, + { + country_short: 'MN', + region: 'Hovd', + city: 'Khovd', + }, + { + country_short: 'KN', + region: 'Saint James Windward', + city: 'Mount Lily', + }, + { + country_short: 'LC', + region: 'Dennery', + city: 'Dennery', + }, + { + country_short: 'KE', + region: 'Tana River', + city: 'Hola', + }, + { + country_short: 'DZ', + region: "M'sila", + city: "'Ain el Melh", + }, + { + country_short: 'NG', + region: 'Taraba', + city: 'Beli', + }, + { + country_short: 'MW', + region: 'Mchinji', + city: 'Mchinji', + }, + { + country_short: 'BF', + region: 'Ioba', + city: 'Dano', + }, + { + country_short: 'BS', + region: 'San Salvador', + city: 'Cockburn Town', + }, + { + country_short: 'ME', + region: 'Tuzi', + city: 'Tuzi', + }, + { + country_short: 'MK', + region: 'Vasilevo', + city: 'Vasilevo', + }, + { + country_short: 'SI', + region: 'Cankova', + city: 'Cankova', + }, + { + country_short: 'SI', + region: 'Vuzenica', + city: 'Vuzenica', + }, + { + country_short: 'SI', + region: 'Kuzma', + city: 'Kuzma', + }, + { + country_short: 'SI', + region: 'Horjul', + city: 'Horjul', + }, + { + country_short: 'SM', + region: 'Serravalle', + city: 'Serravalle', + }, + { + country_short: 'RS', + region: 'Pecki okrug', + city: 'Klina', + }, + { + country_short: 'PH', + region: 'Basilan', + city: 'City of Isabela', + }, + { + country_short: 'AF', + region: 'Sar-e Pul', + city: 'Sar-e Pul', + }, + { + country_short: 'MN', + region: 'Selenge', + city: 'Delgerhaan', + }, + { + country_short: 'PH', + region: 'Biliran', + city: 'Talostosan', + }, + { + country_short: 'LA', + region: 'Xiangkhouang', + city: 'Muang Phonsavan', + }, + { + country_short: 'LA', + region: 'Bokeo', + city: 'Ban Houayxay', + }, + { + country_short: 'LA', + region: 'Bolikhamxai', + city: 'Paksan', + }, + { + country_short: 'LA', + region: 'Xekong', + city: 'Dak Cheung', + }, + { + country_short: 'LA', + region: 'Oudomxai', + city: 'Muang Xai', + }, + { + country_short: 'PH', + region: 'Tawi-Tawi', + city: 'Bongao', + }, + { + country_short: 'PH', + region: 'Lanao del Sur', + city: 'Uyaan', + }, + { + country_short: 'GU', + region: 'Chalan Pago-Ordot', + city: 'Chalan Pago-Ordot Municipality', + }, + { + country_short: 'AF', + region: 'Ghor', + city: 'Shahrak', + }, + { + country_short: 'MH', + region: 'Kwajalein', + city: 'Ebaye', + }, + { + country_short: 'IN', + region: 'Lakshadweep', + city: 'Kavaratti', + }, + { + country_short: 'BT', + region: 'Sarpang', + city: 'Sarpang', + }, + { + country_short: 'PK', + region: 'Gilgit-Baltistan', + city: 'Barishal', + }, + { + country_short: 'BT', + region: 'Dagana', + city: 'Daga', + }, + { + country_short: 'BT', + region: 'Bumthang', + city: 'Jakar', + }, + { + country_short: 'BT', + region: 'Monggar', + city: 'Mongar', + }, + { + country_short: 'BT', + region: 'Lhuentse', + city: 'Lhuentse', + }, + { + country_short: 'GU', + region: 'Yona', + city: 'Yona Municipality', + }, + { + country_short: 'PF', + region: 'Iles Australes', + city: 'Moerai', + }, + { + country_short: 'MV', + region: 'North Ari Atoll', + city: 'Rasdhoo', + }, + { + country_short: 'AF', + region: 'Logar', + city: 'Baraki Barak', + }, + { + country_short: 'LS', + region: 'Leribe', + city: 'Leribe', + }, + { + country_short: 'LS', + region: 'Botha-Bothe', + city: 'Butha-Buthe', + }, + { + country_short: 'LS', + region: 'Mokhotlong', + city: 'Mokhotlong', + }, + { + country_short: 'LS', + region: "Qacha's Nek", + city: "Qacha's Nek", + }, + { + country_short: 'CL', + region: 'Los Rios', + city: 'Valdivia', + }, + { + country_short: 'PA', + region: 'Bocas del Toro', + city: 'Bocas del Toro', + }, + { + country_short: 'TT', + region: 'San Juan-Laventille', + city: 'San Juan', + }, + { + country_short: 'TT', + region: 'Tobago', + city: 'Roxborough', + }, + { + country_short: 'TT', + region: 'San Fernando', + city: 'Mon Repos', + }, + { + country_short: 'HN', + region: 'Yoro', + city: 'El Progreso', + }, + { + country_short: 'BZ', + region: 'Stann Creek', + city: 'Dangriga', + }, + { + country_short: 'DO', + region: 'Santiago Rodriguez', + city: 'Sabaneta', + }, + { + country_short: 'PE', + region: 'San Martin', + city: 'Moyobamba', + }, + { + country_short: 'PE', + region: 'Huanuco', + city: 'Tingo Maria', + }, + { + country_short: 'PY', + region: 'Caazapa', + city: 'General Higinio Morinigo', + }, + { + country_short: 'YE', + region: 'Al Mahwit', + city: 'Shibam', + }, + { + country_short: 'EH', + region: 'Western Sahara', + city: 'Laayoune / El Aaiun', + }, + { + country_short: 'HM', + region: 'Heard Island and McDonald Islands', + city: 'Heard Island and McDonald Islands', + }, + { + country_short: 'HN', + region: 'Copan', + city: 'Copan', + }, + { + country_short: 'TT', + region: 'Arima', + city: 'Arima', + }, + { + country_short: 'PY', + region: 'Misiones', + city: 'Ayolas', + }, + { + country_short: 'HN', + region: 'Valle', + city: 'Nacaome', + }, + { + country_short: 'TT', + region: 'Couva-Tabaquite-Talparo', + city: 'Tabaquite', + }, + { + country_short: 'NI', + region: 'Masaya', + city: 'Masaya', + }, + { + country_short: 'CO', + region: 'Choco', + city: 'Quibdo', + }, + { + country_short: 'GT', + region: 'Huehuetenango', + city: 'Huehuetenango', + }, + { + country_short: 'MT', + region: 'Balzan', + city: 'Balzan', + }, + { + country_short: 'TT', + region: 'Sangre Grande', + city: 'Sangre Grande', + }, + { + country_short: 'DO', + region: 'Hermanas Mirabal', + city: 'Tenares', + }, + { + country_short: 'NI', + region: 'Esteli', + city: 'La Trinidad', + }, + { + country_short: 'GT', + region: 'Chimaltenango', + city: 'Tecpan Guatemala', + }, + { + country_short: 'CO', + region: 'Guainia', + city: 'Inirida', + }, + { + country_short: 'VE', + region: 'Trujillo', + city: 'Betijoque', + }, + { + country_short: 'PY', + region: 'Cordillera', + city: 'Caacupe', + }, + { + country_short: 'SI', + region: 'Radlje ob Dravi', + city: 'Radlje ob Dravi', + }, + { + country_short: 'SI', + region: 'Cerkno', + city: 'Cerkno', + }, + { + country_short: 'DO', + region: 'Elias Pina', + city: 'Comendador', + }, + { + country_short: 'HT', + region: 'Sud', + city: 'Les Cayes', + }, + { + country_short: 'HT', + region: 'Centre', + city: 'Hinche', + }, + { + country_short: 'MX', + region: 'Baja California Sur', + city: 'La Paz', + }, + { + country_short: 'AF', + region: 'Paktika', + city: 'Zarah Sharan', + }, + { + country_short: 'SI', + region: 'Mokronog-Trebelno', + city: 'Mokronog', + }, + { + country_short: 'UY', + region: 'Lavalleja', + city: 'Solis de Mataojo', + }, + { + country_short: 'UY', + region: 'Maldonado', + city: 'Pan de Azucar', + }, + { + country_short: 'UY', + region: 'Colonia', + city: 'Colonia del Sacramento', + }, + { + country_short: 'UY', + region: 'Canelones', + city: 'Las Piedras', + }, + { + country_short: 'UY', + region: 'Rocha', + city: 'Lascano', + }, + { + country_short: 'UY', + region: 'Cerro Largo', + city: 'Acegua', + }, + { + country_short: 'UY', + region: 'Tacuarembo', + city: 'Tacuarembo', + }, + { + country_short: 'CU', + region: 'Matanzas', + city: 'Matanzas', + }, + { + country_short: 'CU', + region: 'Ciego de Avila', + city: 'Villa', + }, + { + country_short: 'CU', + region: 'Cienfuegos', + city: 'Cienfuegos', + }, + { + country_short: 'CU', + region: 'Sancti Spiritus', + city: 'Sancti Spiritus', + }, + { + country_short: 'CU', + region: 'Mayabeque', + city: 'Jamaica', + }, + { + country_short: 'CU', + region: 'Artemisa', + city: 'Bauta', + }, + { + country_short: 'CU', + region: 'Guantanamo', + city: 'Guantanamo', + }, + { + country_short: 'CU', + region: 'Camaguey', + city: 'Camaguey', + }, + { + country_short: 'CU', + region: 'Las Tunas', + city: 'Las Tunas', + }, + { + country_short: 'CU', + region: 'Granma', + city: 'Bayamo', + }, + { + country_short: 'CU', + region: 'Pinar del Rio', + city: 'Pinar del Rio', + }, + { + country_short: 'CU', + region: 'Villa Clara', + city: 'Santa Clara', + }, + { + country_short: 'NI', + region: 'Costa Caribe Norte', + city: 'Alo', + }, + { + country_short: 'NI', + region: 'Nueva Segovia', + city: 'Quilali', + }, + { + country_short: 'SI', + region: 'Solcava', + city: 'Solcava', + }, + { + country_short: 'SI', + region: 'Kobarid', + city: 'Kobarid', + }, + { + country_short: 'SI', + region: 'Tisina', + city: 'Gederovci', + }, + { + country_short: 'SI', + region: 'Rence-Vogrsko', + city: 'Bukovica', + }, + { + country_short: 'SI', + region: 'Hodos', + city: 'Hodos', + }, + { + country_short: 'SI', + region: 'Podlehnik', + city: 'Podlehnik', + }, + { + country_short: 'SI', + region: 'Sveti Andraz v Slovenskih Goricah', + city: 'Vitomarci', + }, + { + country_short: 'SI', + region: 'Cirkulane', + city: 'Pohorje', + }, + { + country_short: 'SI', + region: 'Muta', + city: 'Muta', + }, + { + country_short: 'SI', + region: 'Grad', + city: 'Grad', + }, + { + country_short: 'SI', + region: 'Trnovska Vas', + city: 'Trnovska Vas', + }, + { + country_short: 'MW', + region: 'Chikwawa', + city: 'Chikwawa', + }, + { + country_short: 'SC', + region: 'Grand Anse Praslin', + city: 'Anse Kerlan', + }, + { + country_short: 'UG', + region: 'Moyo', + city: 'Moyo', + }, + { + country_short: 'BI', + region: 'Ngozi', + city: 'Ngozi', + }, + { + country_short: 'TD', + region: 'Chari-Baguirmi', + city: 'Bousso', + }, + { + country_short: 'TD', + region: 'Lac', + city: 'Bol', + }, + { + country_short: 'TD', + region: 'Sila', + city: 'Goz Beida', + }, + { + country_short: 'GA', + region: 'Moyen-Ogooue', + city: 'Lambarene', + }, + { + country_short: 'ML', + region: 'Kayes', + city: 'Kayes Ndi', + }, + { + country_short: 'BI', + region: 'Mwaro', + city: 'Mwaro', + }, + { + country_short: 'LY', + region: 'Nalut', + city: 'Nalut', + }, + { + country_short: 'LA', + region: 'Salavan', + city: 'Salavan', + }, + { + country_short: 'UG', + region: 'Ibanda', + city: 'Bisheshe', + }, + { + country_short: 'UG', + region: 'Rukungiri', + city: 'Rukungiri', + }, + { + country_short: 'UG', + region: 'Buvuma', + city: 'Kitamilo', + }, + { + country_short: 'UG', + region: 'Kitgum', + city: 'Kitgum', + }, + { + country_short: 'UG', + region: 'Koboko', + city: 'Koboko', + }, + { + country_short: 'UG', + region: 'Nakapiripirit', + city: 'Nakapiripirit', + }, + { + country_short: 'UG', + region: 'Pallisa', + city: 'Pallisa', + }, + { + country_short: 'UG', + region: 'Bundibugyo', + city: 'Bundibugyo', + }, + { + country_short: 'UG', + region: 'Kumi', + city: 'Kumi', + }, + { + country_short: 'UG', + region: 'Pader', + city: 'Pader', + }, + { + country_short: 'UG', + region: 'Buyende', + city: 'Buyende', + }, + { + country_short: 'UG', + region: 'Bukomansibi', + city: 'Bukomansimbi', + }, + { + country_short: 'UG', + region: 'Namayingo', + city: 'Namayingo', + }, + { + country_short: 'UG', + region: 'Budaka', + city: 'Budaka', + }, + { + country_short: 'AZ', + region: 'Agsu', + city: 'Aghsu', + }, + { + country_short: 'CG', + region: 'Sangha', + city: 'Ouesso', + }, + { + country_short: 'NI', + region: 'Leon', + city: 'Leon', + }, + { + country_short: 'HT', + region: "Grande'Anse", + city: 'Baraderes', + }, + { + country_short: 'HT', + region: 'Nord', + city: 'Pignon', + }, + { + country_short: 'HT', + region: 'Nord-Ouest', + city: 'Ti Port-de-Paix', + }, + { + country_short: 'TT', + region: 'Point Fortin', + city: 'Point Fortin', + }, + { + country_short: 'TT', + region: 'Siparia', + city: 'Siparia', + }, + { + country_short: 'BO', + region: 'Potosi', + city: 'Potosi', + }, + { + country_short: 'BO', + region: 'Tarija', + city: 'Tarija', + }, + { + country_short: 'UY', + region: 'Paysandu', + city: 'Paysandu', + }, + { + country_short: 'NG', + region: 'Kebbi', + city: 'Birnin Kebbi', + }, + { + country_short: 'NI', + region: 'Chinandega', + city: 'Chinandega', + }, + { + country_short: 'UY', + region: 'Artigas', + city: 'Artigas', + }, + { + country_short: 'UY', + region: 'San Jose', + city: 'Libertad', + }, + { + country_short: 'UY', + region: 'Rivera', + city: 'Tranqueras', + }, + { + country_short: 'UY', + region: 'Rio Negro', + city: 'Fray Bentos', + }, + { + country_short: 'UY', + region: 'Flores', + city: 'Trinidad', + }, + { + country_short: 'UY', + region: 'Soriano', + city: 'Mercedes', + }, + { + country_short: 'UY', + region: 'Florida', + city: 'Florida', + }, + { + country_short: 'UY', + region: 'Treinta y Tres', + city: 'Treinta y Tres', + }, + { + country_short: 'PE', + region: 'Huancavelica', + city: 'Cieneguilla', + }, + { + country_short: 'PE', + region: 'Ayacucho', + city: 'Ayna', + }, + { + country_short: 'PE', + region: 'Apurimac', + city: 'Andahuaylas', + }, + { + country_short: 'BZ', + region: 'Orange Walk', + city: 'Orange Walk', + }, + { + country_short: 'SV', + region: 'San Vicente', + city: 'San Vicente', + }, + { + country_short: 'GY', + region: 'East Berbice-Corentyne', + city: 'New Amsterdam', + }, + { + country_short: 'MW', + region: 'Ntchisi', + city: 'Ntchisi', + }, + { + country_short: 'NG', + region: 'Ebonyi', + city: 'Effium', + }, + { + country_short: 'CF', + region: 'Sangha', + city: 'Nola', + }, + { + country_short: 'BZ', + region: 'Toledo', + city: 'Punta Gorda', + }, + { + country_short: 'AR', + region: 'Tierra del Fuego', + city: 'Ushuaia', + }, + { + country_short: 'HN', + region: 'Intibuca', + city: 'Yamaranguila', + }, + { + country_short: 'PY', + region: 'Paraguari', + city: 'Paraguari', + }, + { + country_short: 'AZ', + region: 'Astara', + city: 'Astara', + }, + { + country_short: 'PF', + region: 'Iles Marquises', + city: 'Atuona', + }, + { + country_short: 'PR', + region: 'Culebra', + city: 'Culebra', + }, + { + country_short: 'PY', + region: 'Neembucu', + city: 'Pilar', + }, + { + country_short: 'PY', + region: 'Presidente Hayes', + city: 'Villa Hayes', + }, + { + country_short: 'SI', + region: 'Odranci', + city: 'Odranci', + }, + { + country_short: 'PE', + region: 'Cajamarca', + city: 'Jaen', + }, + { + country_short: 'PE', + region: 'Tumbes', + city: 'Tumbes', + }, + { + country_short: 'PE', + region: 'Amazonas', + city: 'Bagua Grande', + }, + { + country_short: 'HN', + region: 'Choluteca', + city: 'Ciudad Choluteca', + }, + { + country_short: 'HT', + region: 'Artibonite', + city: 'Desarmes', + }, + { + country_short: 'AF', + region: 'Parwan', + city: 'Jabal os Saraj', + }, + { + country_short: 'VE', + region: 'Guarico', + city: 'Torrealba', + }, + { + country_short: 'VE', + region: 'Apure', + city: 'San Fernando de Apure', + }, + { + country_short: 'DO', + region: 'Pedernales', + city: 'Pedernales', + }, + { + country_short: 'GY', + region: 'Mahaica-Berbice', + city: 'Mahaicony Village', + }, + { + country_short: 'GY', + region: 'Essequibo Islands-West Demerara', + city: 'Parika', + }, + { + country_short: 'GY', + region: 'Upper Demerara-Berbice', + city: 'Linden', + }, + { + country_short: 'GY', + region: 'Barima-Waini', + city: 'Mabaruma', + }, + { + country_short: 'CO', + region: 'Vichada', + city: 'Puerto Carreno', + }, + { + country_short: 'GT', + region: 'San Marcos', + city: 'Catarina', + }, + { + country_short: 'GT', + region: 'Peten', + city: 'Poptun', + }, + { + country_short: 'TT', + region: 'Diego Martin', + city: 'Petit Valley', + }, + { + country_short: 'GT', + region: 'Zacapa', + city: 'Zacapa', + }, + { + country_short: 'GT', + region: 'Santa Rosa', + city: 'Taxisco', + }, + { + country_short: 'GT', + region: 'Baja Verapaz', + city: 'El Chol', + }, + { + country_short: 'GY', + region: 'Potaro-Siparuni', + city: "Saint Mary's", + }, + { + country_short: 'GT', + region: 'Totonicapan', + city: 'Totonicapan', + }, + { + country_short: 'HN', + region: 'Gracias a Dios', + city: 'Puerto Lempira', + }, + { + country_short: 'GT', + region: 'Retalhuleu', + city: 'Santa Cruz Mulua', + }, + { + country_short: 'GU', + region: 'Talofofo', + city: 'Talofofo Municipality', + }, + { + country_short: 'GU', + region: 'Inarajan', + city: 'Inarajan Municipality', + }, + { + country_short: 'AF', + region: 'Paktiya', + city: 'Gardez', + }, + { + country_short: 'SM', + region: 'Faetano', + city: 'Faetano', + }, + { + country_short: 'AZ', + region: 'Tovuz', + city: 'Tovuz', + }, + { + country_short: 'MK', + region: 'Mogila', + city: 'Mogila', + }, + { + country_short: 'NI', + region: 'Madriz', + city: 'Somoto', + }, + { + country_short: 'VE', + region: 'Cojedes', + city: 'San Carlos', + }, + { + country_short: 'NI', + region: 'Rio San Juan', + city: 'San Carlos', + }, + { + country_short: 'NI', + region: 'Jinotega', + city: 'Jinotega', + }, + { + country_short: 'VE', + region: 'Delta Amacuro', + city: 'Tucupita', + }, + { + country_short: 'PA', + region: 'Darien', + city: 'Jaque', + }, + { + country_short: 'SR', + region: 'Wanica', + city: 'Lelydorp', + }, + { + country_short: 'SR', + region: 'Nickerie', + city: 'Nieuw Nickerie', + }, + { + country_short: 'SR', + region: 'Brokopondo', + city: 'Brokopondo', + }, + { + country_short: 'SR', + region: 'Commewijne', + city: 'Nieuw Amsterdam', + }, + { + country_short: 'SR', + region: 'Sipaliwini', + city: 'Gonoe', + }, + { + country_short: 'SR', + region: 'Saramacca', + city: 'Groningen', + }, + { + country_short: 'AZ', + region: 'Gadabay', + city: 'Kyadabek', + }, + { + country_short: 'AZ', + region: 'Saatli', + city: 'Ahmadbayli', + }, + { + country_short: 'UZ', + region: 'Sirdaryo', + city: 'Guliston', + }, + { + country_short: 'PA', + region: 'Guna Yala', + city: 'Airdirgandi', + }, + { + country_short: 'GT', + region: 'El Progreso', + city: 'Guastatoya', + }, + { + country_short: 'VE', + region: 'Dependencias Federales', + city: 'Punta de Garambeo', + }, + { + country_short: 'GY', + region: 'Cuyuni-Mazaruni', + city: 'Bartica', + }, + { + country_short: 'SR', + region: 'Coronie', + city: 'Totness', + }, + { + country_short: 'HT', + region: 'Sud-Est', + city: 'Jacmel', + }, + { + country_short: 'HT', + region: 'Nippes', + city: 'Miragoane', + }, + { + country_short: 'NI', + region: 'Boaco', + city: 'Boaco', + }, + { + country_short: 'SO', + region: 'Hiiraan', + city: 'Beledweyne', + }, + { + country_short: 'SI', + region: 'Mislinja', + city: 'Mislinja', + }, + { + country_short: 'SI', + region: 'Kobilje', + city: 'Kobilje', + }, + { + country_short: 'SI', + region: 'Rogasovci', + city: 'Rogasovci', + }, + { + country_short: 'EE', + region: 'Hiiumaa', + city: 'Kardla', + }, + { + country_short: 'AX', + region: 'Jomala', + city: 'Jomala', + }, + { + country_short: 'AX', + region: 'Eckeroe', + city: 'Eckero', + }, + { + country_short: 'SI', + region: 'Mirna', + city: 'Mirna', + }, + { + country_short: 'ME', + region: 'Cetinje', + city: 'Cetinje', + }, + { + country_short: 'YE', + region: 'Shabwah', + city: "Al 'Aqir", + }, + { + country_short: 'AZ', + region: 'Yardimli', + city: 'Yardimli', + }, + { + country_short: 'KN', + region: 'Saint John Capisterre', + city: 'Tabernacle', + }, + { + country_short: 'MZ', + region: 'Cabo Delgado', + city: 'Montepuez', + }, + { + country_short: 'GM', + region: 'Upper River', + city: 'Basse Santa Su', + }, + { + country_short: 'TG', + region: 'Savanes', + city: 'Sansanne-Mango', + }, + { + country_short: 'TG', + region: 'Centrale', + city: 'Sotouboua', + }, + { + country_short: 'ET', + region: 'Amara', + city: 'Debre Birhan', + }, + { + country_short: 'ET', + region: 'Oromiya', + city: 'Bedele', + }, + { + country_short: 'ET', + region: 'Tigray', + city: "Mek'ele", + }, + { + country_short: 'ET', + region: 'Sumale', + city: 'Jijiga', + }, + { + country_short: 'ET', + region: 'Dire Dawa', + city: 'Dire Dawa', + }, + { + country_short: 'ET', + region: 'Afar', + city: 'Dubti', + }, + { + country_short: 'ET', + region: 'YeDebub Biheroch Bihereseboch na Hizboch', + city: 'Boditi', + }, + { + country_short: 'ET', + region: 'Binshangul Gumuz', + city: 'Asosa', + }, + { + country_short: 'ET', + region: 'Gambela Hizboch', + city: 'Gambela', + }, + { + country_short: 'AF', + region: 'Uruzgan', + city: 'Uruzgan', + }, + { + country_short: 'LS', + region: 'Quthing', + city: 'Quthing', + }, + { + country_short: 'MW', + region: 'Balaka', + city: 'Balaka', + }, + { + country_short: 'CD', + region: 'Kwango', + city: 'Masina', + }, + { + country_short: 'CD', + region: 'Haut-Lomami', + city: 'Bukama', + }, + { + country_short: 'GN', + region: 'Fria', + city: 'Kimbo', + }, + { + country_short: 'GN', + region: 'Mandiana', + city: 'Mandiana', + }, + { + country_short: 'GN', + region: 'Boffa', + city: 'Boffa', + }, + { + country_short: 'GN', + region: 'Labe', + city: 'Labe', + }, + { + country_short: 'LS', + region: 'Mafeteng', + city: 'Mafeteng', + }, + { + country_short: 'ET', + region: 'Hareri Hizb', + city: 'Harar', + }, + { + country_short: 'SC', + region: 'Bel Ombre', + city: 'Bel Ombre', + }, + { + country_short: 'SC', + region: 'Cascade', + city: 'Cascade', + }, + { + country_short: 'LS', + region: "Mohale's Hoek", + city: "Mohale's Hoek", + }, + { + country_short: 'LS', + region: 'Thaba-Tseka', + city: 'Thaba-Tseka', + }, + { + country_short: 'LS', + region: 'Berea', + city: 'Teyateyaneng', + }, + { + country_short: 'GW', + region: 'Oio', + city: 'Mansoa', + }, + { + country_short: 'GW', + region: 'Gabu', + city: 'Gabu', + }, + { + country_short: 'CG', + region: 'Niari', + city: 'Dolisie', + }, + { + country_short: 'CG', + region: 'Bouenza', + city: 'Madingou', + }, + { + country_short: 'SO', + region: 'Bay', + city: 'Baidoa', + }, + { + country_short: 'SO', + region: 'Galguduud', + city: 'Dhuusamarreeb', + }, + { + country_short: 'UG', + region: 'Oyam', + city: 'Oyam', + }, + { + country_short: 'LR', + region: 'Bomi', + city: 'Tubmanburg', + }, + { + country_short: 'SO', + region: 'Nugaal', + city: 'Garoowe', + }, + { + country_short: 'SO', + region: 'Sanaag', + city: 'Laasqoray', + }, + { + country_short: 'SS', + region: 'Lakes', + city: 'Rumbek', + }, + { + country_short: 'NA', + region: 'Kavango West', + city: 'Nkurenkuru', + }, + { + country_short: 'UG', + region: 'Namutumba', + city: 'Namutumba', + }, + { + country_short: 'DJ', + region: 'Arta', + city: 'Arta', + }, + { + country_short: 'DJ', + region: 'Dikhil', + city: 'Dikhil', + }, + { + country_short: 'KM', + region: 'Moheli', + city: 'Ouanani', + }, + { + country_short: 'SB', + region: 'Choiseul', + city: 'Mbatongo', + }, + { + country_short: 'WS', + region: 'Gagaifomauga', + city: 'Matavai', + }, + { + country_short: 'PG', + region: 'West Sepik', + city: 'Vanimo', + }, + { + country_short: 'MN', + region: 'Ovorhangay', + city: 'Arvaikheer', + }, + { + country_short: 'TO', + region: "'Eua", + city: 'Tufurai', + }, + { + country_short: 'PH', + region: 'Apayao', + city: 'Kabugao', + }, + { + country_short: 'PW', + region: 'Airai', + city: 'Ngetkib', + }, + { + country_short: 'PW', + region: 'Ngardmau', + city: 'Ngardmau', + }, + { + country_short: 'PW', + region: 'Kayangel', + city: 'Kayangel', + }, + { + country_short: 'TO', + region: 'Niuas', + city: 'Falehau', + }, + { + country_short: 'TO', + region: "Vava'u", + city: 'Neiafu', + }, + { + country_short: 'LA', + region: 'Xaignabouli', + city: 'Ban Houakhoua', + }, + { + country_short: 'BT', + region: 'Pema Gatshel', + city: 'Pemagatshel', + }, + { + country_short: 'BT', + region: 'Gasa', + city: 'Gasa', + }, + { + country_short: 'PG', + region: 'East New Britain', + city: 'Kokopo', + }, + { + country_short: 'MN', + region: 'Bayanhongor', + city: 'Bayanhongor', + }, + { + country_short: 'WS', + region: 'Atua', + city: 'Lotofaga', + }, + { + country_short: 'NR', + region: 'Aiwo', + city: 'Arijejen', + }, + { + country_short: 'KN', + region: 'Saint Peter Basseterre', + city: 'Canada', + }, + { + country_short: 'KN', + region: 'Saint Anne Sandy Point', + city: 'Fig Tree', + }, + { + country_short: 'DM', + region: 'Saint Joseph', + city: 'Salisbury', + }, + { + country_short: 'DM', + region: 'Saint John', + city: 'Portsmouth', + }, + { + country_short: 'KN', + region: 'Trinity Palmetto Point', + city: 'Challengers', + }, + { + country_short: 'KN', + region: 'Saint Paul Capisterre', + city: 'Heldens', + }, + { + country_short: 'LC', + region: 'Anse la Raye', + city: 'Anse La Raye', + }, + { + country_short: 'AG', + region: 'Barbuda', + city: 'Codrington', + }, + { + country_short: 'FJ', + region: 'Northern', + city: 'Labasa', + }, + { + country_short: 'AZ', + region: 'Daskasan', + city: 'Yukhary-Dashkesan', + }, + { + country_short: 'AZ', + region: 'Haciqabul', + city: 'Aja Kabul', + }, + { + country_short: 'MK', + region: 'Krusevo', + city: 'Krushevo', + }, + { + country_short: 'AX', + region: 'Hammarland', + city: 'Hammarland', + }, + { + country_short: 'AX', + region: 'Saltvik', + city: 'Saltvik', + }, + { + country_short: 'AX', + region: 'Lemland', + city: 'Lemland', + }, +]; diff --git a/packages/js/product-editor/assets/icons/section_attributes.svg b/packages/js/product-editor/assets/icons/section_attributes.svg deleted file mode 100644 index b94fc16fc06..00000000000 --- a/packages/js/product-editor/assets/icons/section_attributes.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/packages/js/product-editor/assets/icons/section_basic.svg b/packages/js/product-editor/assets/icons/section_basic.svg deleted file mode 100644 index cf6a84b463c..00000000000 --- a/packages/js/product-editor/assets/icons/section_basic.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/packages/js/product-editor/assets/icons/section_images.svg b/packages/js/product-editor/assets/icons/section_images.svg deleted file mode 100644 index e48e5cad220..00000000000 --- a/packages/js/product-editor/assets/icons/section_images.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - diff --git a/packages/js/product-editor/assets/icons/section_inventory.svg b/packages/js/product-editor/assets/icons/section_inventory.svg deleted file mode 100644 index 5f130b2abd6..00000000000 --- a/packages/js/product-editor/assets/icons/section_inventory.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/packages/js/product-editor/assets/icons/section_pricing.svg b/packages/js/product-editor/assets/icons/section_pricing.svg deleted file mode 100644 index 46e2ee12101..00000000000 --- a/packages/js/product-editor/assets/icons/section_pricing.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/packages/js/product-editor/assets/icons/section_shipping.svg b/packages/js/product-editor/assets/icons/section_shipping.svg deleted file mode 100644 index f7289a1f9c9..00000000000 --- a/packages/js/product-editor/assets/icons/section_shipping.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - diff --git a/packages/js/product-editor/changelog/add-37984 b/packages/js/product-editor/changelog/add-37984 new file mode 100644 index 00000000000..6a9cf362045 --- /dev/null +++ b/packages/js/product-editor/changelog/add-37984 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Fix validation behavior#37984 diff --git a/packages/js/product-editor/changelog/add-37985 b/packages/js/product-editor/changelog/add-37985 new file mode 100644 index 00000000000..fc2c2d5017d --- /dev/null +++ b/packages/js/product-editor/changelog/add-37985 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add Sale price validation#37985 diff --git a/packages/js/product-editor/changelog/add-38044 b/packages/js/product-editor/changelog/add-38044 new file mode 100644 index 00000000000..25b146641e5 --- /dev/null +++ b/packages/js/product-editor/changelog/add-38044 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Update shipping class block to match new designs#38044 diff --git a/packages/js/product-editor/changelog/add-38097 b/packages/js/product-editor/changelog/add-38097 new file mode 100644 index 00000000000..e70778d9952 --- /dev/null +++ b/packages/js/product-editor/changelog/add-38097 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix spacing between pricing fields diff --git a/packages/js/product-editor/changelog/add-38200 b/packages/js/product-editor/changelog/add-38200 new file mode 100644 index 00000000000..9cee901e3f9 --- /dev/null +++ b/packages/js/product-editor/changelog/add-38200 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Only register blocks when user navigates to the product edit page#38200 diff --git a/packages/js/product-editor/changelog/add-38215 b/packages/js/product-editor/changelog/add-38215 new file mode 100644 index 00000000000..019abe10811 --- /dev/null +++ b/packages/js/product-editor/changelog/add-38215 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Add error specific messages to product save functionality diff --git a/packages/js/product-editor/changelog/add-38248 b/packages/js/product-editor/changelog/add-38248 new file mode 100644 index 00000000000..937374bdd35 --- /dev/null +++ b/packages/js/product-editor/changelog/add-38248 @@ -0,0 +1,4 @@ +Significance: minor +Type: add + +Make DateTimePickerControl a ForwardedRef diff --git a/packages/js/product-editor/changelog/fix-37956 b/packages/js/product-editor/changelog/fix-37956 new file mode 100644 index 00000000000..57e418f138f --- /dev/null +++ b/packages/js/product-editor/changelog/fix-37956 @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Fix summary toolbar positioning and selection on blur diff --git a/packages/js/product-editor/changelog/fix-38020 b/packages/js/product-editor/changelog/fix-38020 new file mode 100644 index 00000000000..d45ce96e163 --- /dev/null +++ b/packages/js/product-editor/changelog/fix-38020 @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Prevent double debouncing of iframe editor callback diff --git a/packages/js/product-editor/changelog/fix-38020-2 b/packages/js/product-editor/changelog/fix-38020-2 new file mode 100644 index 00000000000..3f2c4d7befe --- /dev/null +++ b/packages/js/product-editor/changelog/fix-38020-2 @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Wait for editor changes to be debounced before closing modal diff --git a/packages/js/product-editor/changelog/fix-38021 b/packages/js/product-editor/changelog/fix-38021 new file mode 100644 index 00000000000..6df229ebaf4 --- /dev/null +++ b/packages/js/product-editor/changelog/fix-38021 @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Add editor styles to product editor and iframe editor diff --git a/packages/js/product-editor/changelog/fix-38149 b/packages/js/product-editor/changelog/fix-38149 new file mode 100644 index 00000000000..f793db4eeff --- /dev/null +++ b/packages/js/product-editor/changelog/fix-38149 @@ -0,0 +1,4 @@ +Significance: minor +Type: fix + +Fix double scrollbars on product editor page diff --git a/packages/js/product-editor/changelog/fix-38221 b/packages/js/product-editor/changelog/fix-38221 new file mode 100644 index 00000000000..7c98399959a --- /dev/null +++ b/packages/js/product-editor/changelog/fix-38221 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +List price gets reset on blur when error is shown#38221 diff --git a/packages/js/product-editor/changelog/fix-38242_editor_hidden_in_firefox b/packages/js/product-editor/changelog/fix-38242_editor_hidden_in_firefox new file mode 100644 index 00000000000..9dd9e2a4c53 --- /dev/null +++ b/packages/js/product-editor/changelog/fix-38242_editor_hidden_in_firefox @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Remove css unrelated to the product block editor. diff --git a/packages/js/product-editor/changelog/fix-38244 b/packages/js/product-editor/changelog/fix-38244 new file mode 100644 index 00000000000..627bbf74773 --- /dev/null +++ b/packages/js/product-editor/changelog/fix-38244 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Limit 'email me when stock reaches' field to numerical only#38244 diff --git a/packages/js/product-editor/changelog/fix-image-section-spacing b/packages/js/product-editor/changelog/fix-image-section-spacing new file mode 100644 index 00000000000..0075e40523d --- /dev/null +++ b/packages/js/product-editor/changelog/fix-image-section-spacing @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fixing spacing when no images added on image gallery block. diff --git a/packages/js/product-editor/changelog/fix-product-summary-field-margin b/packages/js/product-editor/changelog/fix-product-summary-field-margin new file mode 100644 index 00000000000..2233dd7dfc2 --- /dev/null +++ b/packages/js/product-editor/changelog/fix-product-summary-field-margin @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Fix product summary field margin. diff --git a/packages/js/product-editor/changelog/fix-tabbed-navigation-37932 b/packages/js/product-editor/changelog/fix-tabbed-navigation-37932 new file mode 100644 index 00000000000..b71572676df --- /dev/null +++ b/packages/js/product-editor/changelog/fix-tabbed-navigation-37932 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Removing WritingFlow component which was suppressing tabbing behavior in form. diff --git a/packages/js/product-editor/changelog/update-37727 b/packages/js/product-editor/changelog/update-37727 new file mode 100644 index 00000000000..4068fbbe7cc --- /dev/null +++ b/packages/js/product-editor/changelog/update-37727 @@ -0,0 +1,4 @@ +Significance: minor +Type: dev + +Fix broken assertion with comma separated list in category select control diff --git a/packages/js/product-editor/changelog/update-37955_disable_drag_and_drop_images_block b/packages/js/product-editor/changelog/update-37955_disable_drag_and_drop_images_block new file mode 100644 index 00000000000..d80356e5bbb --- /dev/null +++ b/packages/js/product-editor/changelog/update-37955_disable_drag_and_drop_images_block @@ -0,0 +1,4 @@ +Significance: patch +Type: update + +Disable dragging and dropping of images within Product Images block. diff --git a/packages/js/product-editor/changelog/update-38043 b/packages/js/product-editor/changelog/update-38043 new file mode 100644 index 00000000000..1a5161be088 --- /dev/null +++ b/packages/js/product-editor/changelog/update-38043 @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Update shipping dimensions image in new product blocks editor diff --git a/packages/js/product-editor/changelog/update-38117 b/packages/js/product-editor/changelog/update-38117 new file mode 100644 index 00000000000..a4b7eb7f667 --- /dev/null +++ b/packages/js/product-editor/changelog/update-38117 @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Remove drag and drop handle in product blocks editor diff --git a/packages/js/product-editor/changelog/update-38260 b/packages/js/product-editor/changelog/update-38260 new file mode 100644 index 00000000000..ff527f97de7 --- /dev/null +++ b/packages/js/product-editor/changelog/update-38260 @@ -0,0 +1,4 @@ +Significance: minor +Type: update + +Adjust styling based on internal styling feedback diff --git a/packages/js/product-editor/changelog/update-parent-categ b/packages/js/product-editor/changelog/update-parent-categ new file mode 100644 index 00000000000..7f05c6c10a5 --- /dev/null +++ b/packages/js/product-editor/changelog/update-parent-categ @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Use SelectTree in the Parent Category field diff --git a/packages/js/product-editor/src/blocks/attributes/editor.scss b/packages/js/product-editor/src/blocks/attributes/editor.scss index 4bb5af8a006..199ada5b2d3 100644 --- a/packages/js/product-editor/src/blocks/attributes/editor.scss +++ b/packages/js/product-editor/src/blocks/attributes/editor.scss @@ -1,21 +1,18 @@ -.wp-block-woocommerce-product-images-field { - .woocommerce-image-gallery { - margin-top: $gap-largest; +.wp-block-woocommerce-product-attributes-field { + .woocommerce-sortable { + padding: 0; } - .woocommerce-media-uploader { - text-align: left; - } - .woocommerce-media-uploader__label { + + .woocommerce-list-item { + background: none; + border: none; + border-bottom: 1px solid $gray-200; + padding-left: 0; + grid-template-columns: 26% auto 90px; + } + + .woocommerce-sortable__handle { display: none; } - .woocommerce-sortable { - margin-top: 0; - padding: 0; - } - &:not(.has-images) { - .woocommerce-sortable { - display: none; - } - } } diff --git a/packages/js/product-editor/src/blocks/checkbox/edit.tsx b/packages/js/product-editor/src/blocks/checkbox/edit.tsx index 64655ac5d89..ff22d1d2335 100644 --- a/packages/js/product-editor/src/blocks/checkbox/edit.tsx +++ b/packages/js/product-editor/src/blocks/checkbox/edit.tsx @@ -45,7 +45,7 @@ export function Edit( { attributes }: { attributes: BlockAttributes } ) { diff --git a/packages/js/product-editor/src/blocks/checkbox/editor.scss b/packages/js/product-editor/src/blocks/checkbox/editor.scss index 472ffb521b5..4e295624fa8 100644 --- a/packages/js/product-editor/src/blocks/checkbox/editor.scss +++ b/packages/js/product-editor/src/blocks/checkbox/editor.scss @@ -3,6 +3,7 @@ .components-base-control__field { display: flex; + align-items: center; } .components-checkbox-control__label { @@ -11,6 +12,8 @@ &-tooltip-icon { margin: -2px 0 0 $gap-small; + display: flex; + align-items: center; } } diff --git a/packages/js/product-editor/src/blocks/images/edit.tsx b/packages/js/product-editor/src/blocks/images/edit.tsx index 16ee7e69385..07a6975093a 100644 --- a/packages/js/product-editor/src/blocks/images/edit.tsx +++ b/packages/js/product-editor/src/blocks/images/edit.tsx @@ -114,6 +114,7 @@ export function Edit() { ) } { const { id: imageId, dataset } = event.target as HTMLElement; @@ -150,11 +151,12 @@ export function Edit() { images.find( ( img ) => media.id === img.id ) === undefined ) { - images[ replaceIndex ] = media as MediaItem; + const newImages = [ ...images ]; + newImages[ replaceIndex ] = media as MediaItem; recordEvent( 'product_images_replace_image_button_click' ); - setImages( images ); + setImages( newImages ); } } } onSelectAsCover={ () => diff --git a/packages/js/product-editor/src/blocks/images/editor.scss b/packages/js/product-editor/src/blocks/images/editor.scss index 4bb5af8a006..5ab57eb05e6 100644 --- a/packages/js/product-editor/src/blocks/images/editor.scss +++ b/packages/js/product-editor/src/blocks/images/editor.scss @@ -1,7 +1,4 @@ .wp-block-woocommerce-product-images-field { - .woocommerce-image-gallery { - margin-top: $gap-largest; - } .woocommerce-media-uploader { text-align: left; } @@ -13,6 +10,12 @@ padding: 0; } + &.has-images { + .woocommerce-image-gallery { + margin-top: $gap-largest; + } + } + &:not(.has-images) { .woocommerce-sortable { display: none; diff --git a/packages/js/product-editor/src/blocks/index.ts b/packages/js/product-editor/src/blocks/index.ts index cb3b4298c0d..78db019ef1d 100644 --- a/packages/js/product-editor/src/blocks/index.ts +++ b/packages/js/product-editor/src/blocks/index.ts @@ -9,10 +9,12 @@ export { init as initSku } from './inventory-sku'; export { init as initName } from './name'; export { init as initPricing } from './pricing'; export { init as initRadio } from './radio'; +export { init as initRegularPrice } from './regular-price'; +export { init as initSalePrice } from './sale-price'; export { init as initScheduleSale } from './schedule-sale'; export { init as initSection } from './section'; +export { init as initShippingClass } from './shipping-class'; export { init as initShippingDimensions } from './shipping-dimensions'; -export { init as initShippingFee } from './shipping-fee'; export { init as initSummary } from './summary'; export { init as initTab } from './tab'; export { init as initInventoryQuantity } from './inventory-quantity'; diff --git a/packages/js/product-editor/src/blocks/inventory-email/edit.tsx b/packages/js/product-editor/src/blocks/inventory-email/edit.tsx index ead26d92099..916b885e33b 100644 --- a/packages/js/product-editor/src/blocks/inventory-email/edit.tsx +++ b/packages/js/product-editor/src/blocks/inventory-email/edit.tsx @@ -3,16 +3,16 @@ */ import { __, sprintf } from '@wordpress/i18n'; import { Link } from '@woocommerce/components'; - +import { Product } from '@woocommerce/data'; import { createElement, Fragment, createInterpolateElement, } from '@wordpress/element'; import { getSetting } from '@woocommerce/settings'; - +import { BlockEditProps } from '@wordpress/blocks'; import { useBlockProps } from '@wordpress/block-editor'; - +import { useInstanceId } from '@wordpress/compose'; import { BaseControl, // @ts-expect-error `__experimentalInputControl` does exist. @@ -23,54 +23,91 @@ import { // eslint-disable-next-line @woocommerce/dependency-group import { useEntityProp } from '@wordpress/core-data'; -export function Edit() { +/** + * Internal dependencies + */ +import { useValidation } from '../../contexts/validation-context'; +import { InventoryEmailBlockAttributes } from './types'; + +export function Edit( { + clientId, +}: BlockEditProps< InventoryEmailBlockAttributes > ) { const blockProps = useBlockProps(); const notifyLowStockAmount = getSetting( 'notifyLowStockAmount', 2 ); - const [ lowStockAmount, setLowStockAmount ] = useEntityProp( + const [ lowStockAmount, setLowStockAmount ] = useEntityProp< number >( 'postType', 'product', 'low_stock_amount' ); + const id = useInstanceId( BaseControl, 'low_stock_amount' ) as string; + + const { + ref: lowStockAmountRef, + error: lowStockAmountValidationError, + validate: validateLowStockAmount, + } = useValidation< Product >( + `low_stock_amount-${ clientId }`, + async function stockQuantityValidator() { + if ( lowStockAmount && lowStockAmount < 0 ) { + return __( + 'This field must be a positive number.', + 'woocommerce' + ); + } + }, + [ lowStockAmount ] + ); + return ( <>
store settings.', - 'woocommerce' - ), - { - link: ( - + help={ + lowStockAmountValidationError || + createInterpolateElement( + __( + 'Make sure to enable notifications in store settings.', + 'woocommerce' ), - } - ) } + { + link: ( + + ), + } + ) + } + className={ + lowStockAmountValidationError && 'has-error' + } > diff --git a/packages/js/product-editor/src/blocks/inventory-email/index.ts b/packages/js/product-editor/src/blocks/inventory-email/index.ts index 59d35906a12..f022db49254 100644 --- a/packages/js/product-editor/src/blocks/inventory-email/index.ts +++ b/packages/js/product-editor/src/blocks/inventory-email/index.ts @@ -1,22 +1,29 @@ +/** + * External dependencies + */ +import { createElement } from '@wordpress/element'; +import { BlockConfiguration } from '@wordpress/blocks'; + /** * Internal dependencies */ -import { initBlock } from '../../utils'; -import metadata from './block.json'; +import { initBlock } from '../../utils/init-blocks'; +import blockConfiguration from './block.json'; import { Edit } from './edit'; +import { InventoryEmailBlockAttributes } from './types'; -const { name } = metadata; +const { name, ...metadata } = + blockConfiguration as BlockConfiguration< InventoryEmailBlockAttributes >; export { metadata, name }; -export const settings = { +export const settings: Partial< + BlockConfiguration< InventoryEmailBlockAttributes > +> = { example: {}, edit: Edit, }; -export const init = () => - initBlock( { - name, - metadata: metadata as never, - settings, - } ); +export function init() { + return initBlock( { name, metadata, settings } ); +} diff --git a/packages/js/product-editor/src/blocks/inventory-email/types.ts b/packages/js/product-editor/src/blocks/inventory-email/types.ts new file mode 100644 index 00000000000..a34d55343f5 --- /dev/null +++ b/packages/js/product-editor/src/blocks/inventory-email/types.ts @@ -0,0 +1,8 @@ +/** + * External dependencies + */ +import { BlockAttributes } from '@wordpress/blocks'; + +export interface InventoryEmailBlockAttributes extends BlockAttributes { + name: string; +} diff --git a/packages/js/product-editor/src/blocks/inventory-quantity/edit.tsx b/packages/js/product-editor/src/blocks/inventory-quantity/edit.tsx index 84ae08c9aa6..935e7d1bc30 100644 --- a/packages/js/product-editor/src/blocks/inventory-quantity/edit.tsx +++ b/packages/js/product-editor/src/blocks/inventory-quantity/edit.tsx @@ -1,6 +1,7 @@ /** * External dependencies */ +import { Product } from '@woocommerce/data'; import { BlockEditProps } from '@wordpress/blocks'; import { useBlockProps } from '@wordpress/block-editor'; import { useInstanceId } from '@wordpress/compose'; @@ -17,10 +18,11 @@ import { * Internal dependencies */ import { TrackInventoryBlockAttributes } from './types'; +import { useValidation } from '../../contexts/validation-context'; -import { useValidation } from '../../hooks/use-validation'; - -export function Edit( {}: BlockEditProps< TrackInventoryBlockAttributes > ) { +export function Edit( { + clientId, +}: BlockEditProps< TrackInventoryBlockAttributes > ) { const blockProps = useBlockProps(); const [ manageStock ] = useEntityProp< boolean >( @@ -40,16 +42,21 @@ export function Edit( {}: BlockEditProps< TrackInventoryBlockAttributes > ) { 'product_stock_quantity' ) as string; - const stockQuantityValidationError = useValidation( - 'product/stock_quantity', - function stockQuantityValidator() { + const { + ref: stockQuantityRef, + error: stockQuantityValidationError, + validate: validateStockQuantity, + } = useValidation< Product >( + `stock_quantity-${ clientId }`, + async function stockQuantityValidator() { if ( manageStock && stockQuantity && stockQuantity < 0 ) { return __( 'Stock quantity must be a positive number.', 'woocommerce' ); } - } + }, + [ manageStock, stockQuantity ] ); useEffect( () => { @@ -72,9 +79,11 @@ export function Edit( {}: BlockEditProps< TrackInventoryBlockAttributes > ) { diff --git a/packages/js/product-editor/src/blocks/name/edit.tsx b/packages/js/product-editor/src/blocks/name/edit.tsx index d1c257c6b22..753e9f8db79 100644 --- a/packages/js/product-editor/src/blocks/name/edit.tsx +++ b/packages/js/product-editor/src/blocks/name/edit.tsx @@ -35,7 +35,7 @@ import { useEntityProp, useEntityId } from '@wordpress/core-data'; */ import { AUTO_DRAFT_NAME } from '../../utils'; import { EditProductLinkModal } from '../../components/edit-product-link-modal'; -import { useValidation } from '../../hooks/use-validation'; +import { useValidation } from '../../contexts/validation-context'; export function Edit() { const blockProps = useBlockProps(); @@ -77,9 +77,13 @@ export function Edit() { } ); - const nameValidationError = useValidation( - 'product/name', - function nameValidator() { + const { + ref: nameRef, + error: nameValidationError, + validate: validateName, + } = useValidation< Product >( + 'name', + async function nameValidator() { if ( ! name || name === AUTO_DRAFT_NAME ) { return __( 'This field is required.', 'woocommerce' ); } @@ -90,7 +94,8 @@ export function Edit() { 'woocommerce' ); } - } + }, + [ name ] ); const setSkuIfEmpty = () => { @@ -153,6 +158,7 @@ export function Edit() { > { + setSkuIfEmpty(); + validateName(); + } } /> diff --git a/packages/js/product-editor/src/blocks/pricing/edit.tsx b/packages/js/product-editor/src/blocks/pricing/edit.tsx index ca4291243b0..4aeade0ae4d 100644 --- a/packages/js/product-editor/src/blocks/pricing/edit.tsx +++ b/packages/js/product-editor/src/blocks/pricing/edit.tsx @@ -2,18 +2,13 @@ * External dependencies */ import { Link } from '@woocommerce/components'; -import { CurrencyContext } from '@woocommerce/currency'; import { getNewPath } from '@woocommerce/navigation'; import { recordEvent } from '@woocommerce/tracks'; import { useBlockProps } from '@wordpress/block-editor'; import { BlockEditProps } from '@wordpress/blocks'; import { useInstanceId } from '@wordpress/compose'; import { useEntityProp } from '@wordpress/core-data'; -import { - createElement, - useContext, - createInterpolateElement, -} from '@wordpress/element'; +import { createElement, createInterpolateElement } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import { BaseControl, @@ -25,7 +20,6 @@ import { * Internal dependencies */ import { useCurrencyInputProps } from '../../hooks/use-currency-input-props'; -import { formatCurrencyDisplayValue } from '../../utils'; import { PricingBlockAttributes } from './types'; export function Edit( { @@ -38,12 +32,9 @@ export function Edit( { 'product', name ); - const context = useContext( CurrencyContext ); - const { getCurrencyConfig, formatAmount } = context; - const currencyConfig = getCurrencyConfig(); const inputProps = useCurrencyInputProps( { value: price, - setValue: setPrice, + onChange: setPrice, } ); const interpolatedHelp = help @@ -71,13 +62,7 @@ export function Edit( { { ...inputProps } id={ priceId } name={ name } - onChange={ setPrice } label={ label || __( 'Price', 'woocommerce' ) } - value={ formatCurrencyDisplayValue( - String( price ), - currencyConfig, - formatAmount - ) } />
diff --git a/packages/js/product-editor/src/blocks/regular-price/block.json b/packages/js/product-editor/src/blocks/regular-price/block.json new file mode 100644 index 00000000000..70c3c995030 --- /dev/null +++ b/packages/js/product-editor/src/blocks/regular-price/block.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "woocommerce/product-regular-price-field", + "description": "A product price block with currency display.", + "title": "Product regular price", + "category": "widgets", + "keywords": [ "products", "price" ], + "textdomain": "default", + "attributes": { + "label": { + "type": "string", + "__experimentalRole": "content" + }, + "help": { + "type": "string" + } + }, + "supports": { + "align": false, + "html": false, + "multiple": false, + "reusable": false, + "inserter": false, + "lock": false, + "__experimentalToolbar": false + }, + "editorStyle": "file:./editor.css" +} diff --git a/packages/js/product-editor/src/blocks/regular-price/edit.tsx b/packages/js/product-editor/src/blocks/regular-price/edit.tsx new file mode 100644 index 00000000000..9090de1adba --- /dev/null +++ b/packages/js/product-editor/src/blocks/regular-price/edit.tsx @@ -0,0 +1,120 @@ +/** + * External dependencies + */ +import classNames from 'classnames'; +import { Link } from '@woocommerce/components'; +import { Product } from '@woocommerce/data'; +import { getNewPath } from '@woocommerce/navigation'; +import { recordEvent } from '@woocommerce/tracks'; +import { useBlockProps } from '@wordpress/block-editor'; +import { BlockEditProps } from '@wordpress/blocks'; +import { useInstanceId } from '@wordpress/compose'; +import { useEntityProp } from '@wordpress/core-data'; +import { createElement, createInterpolateElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { + BaseControl, + // @ts-expect-error `__experimentalInputControl` does exist. + __experimentalInputControl as InputControl, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { useValidation } from '../../contexts/validation-context'; +import { useCurrencyInputProps } from '../../hooks/use-currency-input-props'; +import { SalePriceBlockAttributes } from './types'; + +export function Edit( { + attributes, + clientId, +}: BlockEditProps< SalePriceBlockAttributes > ) { + const blockProps = useBlockProps(); + const { label, help } = attributes; + const [ regularPrice, setRegularPrice ] = useEntityProp< string >( + 'postType', + 'product', + 'regular_price' + ); + const [ salePrice ] = useEntityProp< string >( + 'postType', + 'product', + 'sale_price' + ); + const inputProps = useCurrencyInputProps( { + value: regularPrice, + onChange: setRegularPrice, + } ); + + const interpolatedHelp = help + ? createInterpolateElement( help, { + PricingTab: ( + { + recordEvent( 'product_pricing_help_click' ); + } } + /> + ), + } ) + : null; + + const regularPriceId = useInstanceId( + BaseControl, + 'wp-block-woocommerce-product-regular-price-field' + ) as string; + + const { + ref: regularPriceRef, + error: regularPriceValidationError, + validate: validateRegularPrice, + } = useValidation< Product >( + `regular_price-${ clientId }`, + async function regularPriceValidator() { + const listPrice = Number.parseFloat( regularPrice ); + if ( listPrice ) { + if ( listPrice < 0 ) { + return __( + 'List price must be greater than or equals to zero.', + 'woocommerce' + ); + } + if ( + salePrice && + listPrice <= Number.parseFloat( salePrice ) + ) { + return __( + 'List price must be greater than the sale price.', + 'woocommerce' + ); + } + } + }, + [ regularPrice, salePrice ] + ); + + return ( +
+ + + +
+ ); +} diff --git a/packages/js/product-editor/src/blocks/regular-price/editor.scss b/packages/js/product-editor/src/blocks/regular-price/editor.scss new file mode 100644 index 00000000000..9115cf687cd --- /dev/null +++ b/packages/js/product-editor/src/blocks/regular-price/editor.scss @@ -0,0 +1,11 @@ +.wp-block-woocommerce-product-regular-price-field { + .components-currency-control { + .components-input-control__prefix { + color: $gray-700; + } + + .components-input-control__input { + text-align: right; + } + } +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/regular-price/index.ts b/packages/js/product-editor/src/blocks/regular-price/index.ts new file mode 100644 index 00000000000..f838788b632 --- /dev/null +++ b/packages/js/product-editor/src/blocks/regular-price/index.ts @@ -0,0 +1,28 @@ +/** + * External dependencies + */ +import { BlockConfiguration } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import { initBlock } from '../../utils/init-blocks'; +import blockConfiguration from './block.json'; +import { Edit } from './edit'; +import { SalePriceBlockAttributes } from './types'; + +const { name, ...metadata } = + blockConfiguration as BlockConfiguration< SalePriceBlockAttributes >; + +export { metadata, name }; + +export const settings: Partial< + BlockConfiguration< SalePriceBlockAttributes > +> = { + example: {}, + edit: Edit, +}; + +export function init() { + return initBlock( { name, metadata, settings } ); +} diff --git a/packages/js/product-editor/src/blocks/regular-price/types.ts b/packages/js/product-editor/src/blocks/regular-price/types.ts new file mode 100644 index 00000000000..99ceaa60e80 --- /dev/null +++ b/packages/js/product-editor/src/blocks/regular-price/types.ts @@ -0,0 +1,9 @@ +/** + * External dependencies + */ +import { BlockAttributes } from '@wordpress/blocks'; + +export interface SalePriceBlockAttributes extends BlockAttributes { + label: string; + help?: string; +} diff --git a/packages/js/product-editor/src/blocks/sale-price/block.json b/packages/js/product-editor/src/blocks/sale-price/block.json new file mode 100644 index 00000000000..51babacca28 --- /dev/null +++ b/packages/js/product-editor/src/blocks/sale-price/block.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 2, + "name": "woocommerce/product-sale-price-field", + "description": "A product price block with currency display.", + "title": "Product sale price", + "category": "widgets", + "keywords": [ "products", "price" ], + "textdomain": "default", + "attributes": { + "label": { + "type": "string", + "__experimentalRole": "content" + }, + "help": { + "type": "string" + } + }, + "supports": { + "align": false, + "html": false, + "multiple": false, + "reusable": false, + "inserter": false, + "lock": false, + "__experimentalToolbar": false + }, + "editorStyle": "file:./editor.css" +} diff --git a/packages/js/product-editor/src/blocks/sale-price/edit.tsx b/packages/js/product-editor/src/blocks/sale-price/edit.tsx new file mode 100644 index 00000000000..e96691d7e65 --- /dev/null +++ b/packages/js/product-editor/src/blocks/sale-price/edit.tsx @@ -0,0 +1,102 @@ +/** + * External dependencies + */ +import classNames from 'classnames'; +import { Product } from '@woocommerce/data'; +import { useBlockProps } from '@wordpress/block-editor'; +import { BlockEditProps } from '@wordpress/blocks'; +import { useInstanceId } from '@wordpress/compose'; +import { useEntityProp } from '@wordpress/core-data'; +import { createElement } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { + BaseControl, + // @ts-expect-error `__experimentalInputControl` does exist. + __experimentalInputControl as InputControl, +} from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { useValidation } from '../../contexts/validation-context'; +import { useCurrencyInputProps } from '../../hooks/use-currency-input-props'; +import { SalePriceBlockAttributes } from './types'; + +export function Edit( { + attributes, + clientId, +}: BlockEditProps< SalePriceBlockAttributes > ) { + const blockProps = useBlockProps(); + const { label, help } = attributes; + const [ regularPrice ] = useEntityProp< string >( + 'postType', + 'product', + 'regular_price' + ); + const [ salePrice, setSalePrice ] = useEntityProp< string >( + 'postType', + 'product', + 'sale_price' + ); + const inputProps = useCurrencyInputProps( { + value: salePrice, + onChange: setSalePrice, + } ); + + const salePriceId = useInstanceId( + BaseControl, + 'wp-block-woocommerce-product-sale-price-field' + ) as string; + + const { + ref: salePriceRef, + error: salePriceValidationError, + validate: validateSalePrice, + } = useValidation< Product >( + `sale-price-${ clientId }`, + async function salePriceValidator() { + if ( salePrice ) { + if ( Number.parseFloat( salePrice ) < 0 ) { + return __( + 'Sale price must be greater than or equals to zero.', + 'woocommerce' + ); + } + const listPrice = Number.parseFloat( regularPrice ); + if ( + ! listPrice || + listPrice <= Number.parseFloat( salePrice ) + ) { + return __( + 'Sale price must be lower than the list price.', + 'woocommerce' + ); + } + } + }, + [ regularPrice, salePrice ] + ); + + return ( +
+ + + +
+ ); +} diff --git a/packages/js/product-editor/src/blocks/sale-price/editor.scss b/packages/js/product-editor/src/blocks/sale-price/editor.scss new file mode 100644 index 00000000000..5d1cf4d7d84 --- /dev/null +++ b/packages/js/product-editor/src/blocks/sale-price/editor.scss @@ -0,0 +1,11 @@ +.wp-block-woocommerce-product-sale-price-field { + .components-currency-control { + .components-input-control__prefix { + color: $gray-700; + } + + .components-input-control__input { + text-align: right; + } + } +} \ No newline at end of file diff --git a/packages/js/product-editor/src/blocks/sale-price/index.ts b/packages/js/product-editor/src/blocks/sale-price/index.ts new file mode 100644 index 00000000000..f838788b632 --- /dev/null +++ b/packages/js/product-editor/src/blocks/sale-price/index.ts @@ -0,0 +1,28 @@ +/** + * External dependencies + */ +import { BlockConfiguration } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import { initBlock } from '../../utils/init-blocks'; +import blockConfiguration from './block.json'; +import { Edit } from './edit'; +import { SalePriceBlockAttributes } from './types'; + +const { name, ...metadata } = + blockConfiguration as BlockConfiguration< SalePriceBlockAttributes >; + +export { metadata, name }; + +export const settings: Partial< + BlockConfiguration< SalePriceBlockAttributes > +> = { + example: {}, + edit: Edit, +}; + +export function init() { + return initBlock( { name, metadata, settings } ); +} diff --git a/packages/js/product-editor/src/blocks/sale-price/types.ts b/packages/js/product-editor/src/blocks/sale-price/types.ts new file mode 100644 index 00000000000..99ceaa60e80 --- /dev/null +++ b/packages/js/product-editor/src/blocks/sale-price/types.ts @@ -0,0 +1,9 @@ +/** + * External dependencies + */ +import { BlockAttributes } from '@wordpress/blocks'; + +export interface SalePriceBlockAttributes extends BlockAttributes { + label: string; + help?: string; +} diff --git a/packages/js/product-editor/src/blocks/schedule-sale/edit.tsx b/packages/js/product-editor/src/blocks/schedule-sale/edit.tsx index da1686ba1d6..ee3a62ce012 100644 --- a/packages/js/product-editor/src/blocks/schedule-sale/edit.tsx +++ b/packages/js/product-editor/src/blocks/schedule-sale/edit.tsx @@ -2,6 +2,7 @@ * External dependencies */ import { DateTimePickerControl } from '@woocommerce/components'; +import { Product } from '@woocommerce/data'; import { recordEvent } from '@woocommerce/tracks'; import { useBlockProps } from '@wordpress/block-editor'; import { BlockEditProps } from '@wordpress/blocks'; @@ -19,9 +20,11 @@ import { getSettings } from '@wordpress/date'; * Internal dependencies */ import { ScheduleSalePricingBlockAttributes } from './types'; -import { useValidation } from '../../hooks/use-validation'; +import { useValidation } from '../../contexts/validation-context'; -export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > ) { +export function Edit( { + clientId, +}: BlockEditProps< ScheduleSalePricingBlockAttributes > ) { const blockProps = useBlockProps(); const dateTimeFormat = getSettings().formats.datetime; @@ -84,9 +87,13 @@ export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > ) const _dateOnSaleFrom = moment( dateOnSaleFromGmt, moment.ISO_8601, true ); const _dateOnSaleTo = moment( dateOnSaleToGmt, moment.ISO_8601, true ); - const dateOnSaleFromGmtValidationError = useValidation( - 'product/date_on_sale_from_gmt', - function dateOnSaleFromValidator() { + const { + ref: dateOnSaleFromGmtRef, + error: dateOnSaleFromGmtValidationError, + validate: validateDateOnSaleFromGmt, + } = useValidation< Product >( + `date_on_sale_from_gmt-${ clientId }`, + async function dateOnSaleFromValidator() { if ( showScheduleSale && dateOnSaleFromGmt ) { if ( ! _dateOnSaleFrom.isValid() ) { return __( 'Please enter a valid date.', 'woocommerce' ); @@ -99,12 +106,17 @@ export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > ) ); } } - } + }, + [ showScheduleSale, dateOnSaleFromGmt, _dateOnSaleFrom, _dateOnSaleTo ] ); - const dateOnSaleToGmtValidationError = useValidation( - 'product/date_on_sale_to_gmt', - function dateOnSaleToValidator() { + const { + ref: dateOnSaleToGmtRef, + error: dateOnSaleToGmtValidationError, + validate: validateDateOnSaleToGmt, + } = useValidation< Product >( + `date_on_sale_to_gmt-${ clientId }`, + async function dateOnSaleToValidator() { if ( showScheduleSale && dateOnSaleToGmt ) { if ( ! _dateOnSaleTo.isValid() ) { return __( 'Please enter a valid date.', 'woocommerce' ); @@ -117,7 +129,8 @@ export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > ) ); } } - } + }, + [ showScheduleSale, dateOnSaleFromGmt, _dateOnSaleFrom, _dateOnSaleTo ] ); return ( @@ -133,6 +146,9 @@ export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > )
+ } label={ __( 'From', 'woocommerce' ) } placeholder={ __( 'Sale start date and time (optional)', @@ -145,11 +161,15 @@ export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > ) dateOnSaleFromGmtValidationError && 'has-error' } help={ dateOnSaleFromGmtValidationError as string } + onBlur={ validateDateOnSaleFromGmt } />
+ } label={ __( 'To', 'woocommerce' ) } placeholder={ __( 'Sale end date and time (optional)', @@ -164,6 +184,7 @@ export function Edit( {}: BlockEditProps< ScheduleSalePricingBlockAttributes > ) .toISOString() ) } + onBlur={ validateDateOnSaleToGmt } className={ dateOnSaleToGmtValidationError && 'has-error' } diff --git a/packages/js/product-editor/src/blocks/schedule-sale/editor.scss b/packages/js/product-editor/src/blocks/schedule-sale/editor.scss index 8b4d06af52b..0d1cb54d151 100644 --- a/packages/js/product-editor/src/blocks/schedule-sale/editor.scss +++ b/packages/js/product-editor/src/blocks/schedule-sale/editor.scss @@ -1,6 +1,21 @@ .wp-block-woocommerce-product-schedule-sale-fields { + margin-bottom: $gap-large; + + .components-toggle-control { + margin-bottom: $gap; + padding-bottom: $gap-smaller; + } + .components-toggle-control__label { display: flex; align-items: center; } } + +.wp-block-woocommerce-product-section { + > .block-editor-inner-blocks > .block-editor-block-list__layout { + > .wp-block.wp-block-woocommerce-product-schedule-sale-fields:not( :first-child ) { + margin-top: $gap; + } + } +} diff --git a/packages/js/product-editor/src/blocks/section/block.json b/packages/js/product-editor/src/blocks/section/block.json index e915bab3cb9..0fc58c9243d 100644 --- a/packages/js/product-editor/src/blocks/section/block.json +++ b/packages/js/product-editor/src/blocks/section/block.json @@ -14,9 +14,6 @@ "description": { "type": "string", "__experimentalRole": "content" - }, - "icon": { - "type": "object" } }, "supports": { diff --git a/packages/js/product-editor/src/blocks/section/edit.tsx b/packages/js/product-editor/src/blocks/section/edit.tsx index 5169609191b..910d6ca5cfd 100644 --- a/packages/js/product-editor/src/blocks/section/edit.tsx +++ b/packages/js/product-editor/src/blocks/section/edit.tsx @@ -8,13 +8,11 @@ import { InnerBlocks, useBlockProps } from '@wordpress/block-editor'; /** * Internal dependencies */ -import { BlockIcon } from '../../components/block-icon'; import { SectionBlockAttributes } from './types'; import { sanitizeHTML } from '../../utils/sanitize-html'; export function Edit( { attributes, - clientId, }: BlockEditProps< SectionBlockAttributes > ) { const blockProps = useBlockProps(); const { description, title } = attributes; @@ -22,7 +20,6 @@ export function Edit( { return (

- { title }

{ description && ( diff --git a/packages/js/product-editor/src/blocks/section/editor.scss b/packages/js/product-editor/src/blocks/section/editor.scss index 039b05a42db..51ef2351e6c 100644 --- a/packages/js/product-editor/src/blocks/section/editor.scss +++ b/packages/js/product-editor/src/blocks/section/editor.scss @@ -6,30 +6,21 @@ padding-top: 64px; } - &__title { + .wp-block-woocommerce-product-section__title { margin-top: 0; margin-bottom: 0; - font-size: 24px; + font-size: 20px; font-weight: 500; color: $gray-900; display: inline-flex; align-items: center; - - .block-editor-block-icon { - margin-right: 14px; - - > div { - display: flex; - align-items: center; - justify-content: center; - } - } } &__description { margin-top: $gap-small; font-size: 13px; - color: $gray-900; + color: $gray-700; + line-height: 1.5; } } diff --git a/packages/js/product-editor/src/blocks/shipping-fee/block.json b/packages/js/product-editor/src/blocks/shipping-class/block.json similarity index 68% rename from packages/js/product-editor/src/blocks/shipping-fee/block.json rename to packages/js/product-editor/src/blocks/shipping-class/block.json index 984cacd6165..124054818db 100644 --- a/packages/js/product-editor/src/blocks/shipping-fee/block.json +++ b/packages/js/product-editor/src/blocks/shipping-class/block.json @@ -1,11 +1,11 @@ { "$schema": "https://schemas.wp.org/trunk/block.json", "apiVersion": 2, - "name": "woocommerce/product-shipping-fee-fields", - "title": "Product shipping fee fields", + "name": "woocommerce/product-shipping-class-field", + "title": "Product shipping class field", "category": "woocommerce", - "description": "The product shipping fee fields.", - "keywords": [ "products", "shipping", "fee" ], + "description": "The product shipping class field.", + "keywords": [ "products", "shipping", "class" ], "textdomain": "default", "attributes": { "title": { diff --git a/packages/js/product-editor/src/blocks/shipping-fee/edit.tsx b/packages/js/product-editor/src/blocks/shipping-class/edit.tsx similarity index 55% rename from packages/js/product-editor/src/blocks/shipping-fee/edit.tsx rename to packages/js/product-editor/src/blocks/shipping-class/edit.tsx index 377dce3f18b..85061e49b52 100644 --- a/packages/js/product-editor/src/blocks/shipping-fee/edit.tsx +++ b/packages/js/product-editor/src/blocks/shipping-class/edit.tsx @@ -18,19 +18,15 @@ import { Fragment, createElement, createInterpolateElement, - useEffect, useState, } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; -import classNames from 'classnames'; import { useEntityProp } from '@wordpress/core-data'; /** * Internal dependencies */ -import { ShippingFeeBlockAttributes } from './types'; -import { useValidation } from '../../hooks/use-validation'; -import { RadioField } from '../../components/radio-field'; +import { ShippingClassBlockAttributes } from './types'; import { AddNewShippingClassModal } from '../../components'; import { ADD_NEW_SHIPPING_CLASS_OPTION_VALUE } from '../../constants'; @@ -38,9 +34,6 @@ type ServerErrorResponse = { code: string; }; -const FOLLOW_CLASS_OPTION_VALUE = 'follow_class'; -const FREE_SHIPPING_OPTION_VALUE = 'free_shipping'; - export const DEFAULT_SHIPPING_CLASS_OPTIONS: SelectControl.Option[] = [ { value: '', label: __( 'No shipping class', 'woocommerce' ) }, { @@ -58,17 +51,6 @@ function mapShippingClassToSelectOption( } ) ); } -const options = [ - { - label: __( 'Follow class', 'woocommerce' ), - value: FOLLOW_CLASS_OPTION_VALUE, - }, - { - label: __( 'Free shipping', 'woocommerce' ), - value: FREE_SHIPPING_OPTION_VALUE, - }, -]; - function extractDefaultShippingClassFromProduct( categories?: PartialProduct[ 'categories' ], shippingClasses?: ProductShippingClass[] @@ -87,19 +69,12 @@ function extractDefaultShippingClassFromProduct( } } -export function Edit( { - attributes, -}: BlockEditProps< ShippingFeeBlockAttributes > ) { - const { title } = attributes; +export function Edit( {}: BlockEditProps< ShippingClassBlockAttributes > ) { const [ showShippingClassModal, setShowShippingClassModal ] = useState( false ); const blockProps = useBlockProps(); - const [ option, setOption ] = useState< string >( - FREE_SHIPPING_OPTION_VALUE - ); - const { createProductShippingClass, invalidateResolution } = useDispatch( EXPERIMENTAL_PRODUCT_SHIPPING_CLASSES_STORE_NAME ); @@ -149,114 +124,71 @@ export function Edit( { }; }, [] ); - const shippingClassControlId = useInstanceId( BaseControl ) as string; - - const shippingClassValidationError = useValidation( - 'product/shipping_class', - function shippingClassValidator() { - if ( option === FOLLOW_CLASS_OPTION_VALUE && ! shippingClass ) { - return __( 'The shipping class is required.', 'woocommerce' ); - } - } - ); - - function handleOptionChange( value: string ) { - setOption( value ); - - if ( value === FOLLOW_CLASS_OPTION_VALUE ) { - const [ firstShippingClass ] = shippingClasses; - setShippingClass( firstShippingClass?.slug ?? '' ); - } else { - setShippingClass( '' ); - } - } - - useEffect( () => { - if ( shippingClass ) { - setOption( FOLLOW_CLASS_OPTION_VALUE ); - } - }, [ shippingClass ] ); + const shippingClassControlId = useInstanceId( + BaseControl, + 'wp-block-woocommerce-product-shipping-class-field' + ) as string; return (
- { + if ( + value === ADD_NEW_SHIPPING_CLASS_OPTION_VALUE + ) { + setShowShippingClassModal( true ); + return; + } + setShippingClass( value ); + } } + label={ __( 'Shipping class', 'woocommerce' ) } + options={ [ + ...DEFAULT_SHIPPING_CLASS_OPTIONS, + ...mapShippingClassToSelectOption( + shippingClasses ?? [] + ), + ] } + help={ createInterpolateElement( + __( + 'Manage shipping classes and rates in global settings.', + 'woocommerce' + ), + { + Link: ( + { + recordEvent( + 'product_shipping_global_settings_link_click' + ); + } } + > + + + ), + } + ) } />
+ +
- { option === FOLLOW_CLASS_OPTION_VALUE && ( -
-
- { - if ( - value === - ADD_NEW_SHIPPING_CLASS_OPTION_VALUE - ) { - setShowShippingClassModal( true ); - return; - } - setShippingClass( value ); - } } - label={ __( 'Shipping class', 'woocommerce' ) } - options={ [ - ...DEFAULT_SHIPPING_CLASS_OPTIONS, - ...mapShippingClassToSelectOption( - shippingClasses ?? [] - ), - ] } - help={ - shippingClassValidationError || - createInterpolateElement( - __( - 'Manage shipping classes and rates in global settings.', - 'woocommerce' - ), - { - Link: ( - { - recordEvent( - 'product_shipping_global_settings_link_click' - ); - } } - > - - - ), - } - ) - } - /> -
- -
-
- ) } { showShippingClassModal && ( ; + blockConfiguration as BlockConfiguration< ShippingClassBlockAttributes >; export { metadata, name }; export const settings: Partial< - BlockConfiguration< ShippingFeeBlockAttributes > + BlockConfiguration< ShippingClassBlockAttributes > > = { example: {}, edit: Edit, diff --git a/packages/js/product-editor/src/blocks/shipping-fee/types.ts b/packages/js/product-editor/src/blocks/shipping-class/types.ts similarity index 59% rename from packages/js/product-editor/src/blocks/shipping-fee/types.ts rename to packages/js/product-editor/src/blocks/shipping-class/types.ts index 90877619db6..4e2daa9c8fc 100644 --- a/packages/js/product-editor/src/blocks/shipping-fee/types.ts +++ b/packages/js/product-editor/src/blocks/shipping-class/types.ts @@ -3,6 +3,6 @@ */ import { BlockAttributes } from '@wordpress/blocks'; -export interface ShippingFeeBlockAttributes extends BlockAttributes { +export interface ShippingClassBlockAttributes extends BlockAttributes { title: string; } diff --git a/packages/js/product-editor/src/blocks/shipping-dimensions/edit.tsx b/packages/js/product-editor/src/blocks/shipping-dimensions/edit.tsx index c162f637a74..16a82d972de 100644 --- a/packages/js/product-editor/src/blocks/shipping-dimensions/edit.tsx +++ b/packages/js/product-editor/src/blocks/shipping-dimensions/edit.tsx @@ -3,7 +3,11 @@ */ import { useBlockProps } from '@wordpress/block-editor'; import { BlockEditProps } from '@wordpress/blocks'; -import { OPTIONS_STORE_NAME, ProductDimensions } from '@woocommerce/data'; +import { + OPTIONS_STORE_NAME, + Product, + ProductDimensions, +} from '@woocommerce/data'; import { useInstanceId } from '@wordpress/compose'; import { useEntityProp } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; @@ -29,9 +33,11 @@ import { HighlightSides, ShippingDimensionsImage, } from '../../components/shipping-dimensions-image'; -import { useValidation } from '../../hooks/use-validation'; +import { useValidation } from '../../contexts/validation-context'; -export function Edit( {}: BlockEditProps< ShippingDimensionsBlockAttributes > ) { +export function Edit( { + clientId, +}: BlockEditProps< ShippingDimensionsBlockAttributes > ) { const blockProps = useBlockProps(); const [ dimensions, setDimensions ] = @@ -79,12 +85,70 @@ export function Edit( {}: BlockEditProps< ShippingDimensionsBlockAttributes > ) }; } + const { + ref: dimensionsWidthRef, + error: dimensionsWidthValidationError, + validate: validateDimensionsWidth, + } = useValidation< Product >( + `dimensions_width-${ clientId }`, + async function dimensionsWidthValidator() { + if ( dimensions?.width && +dimensions.width <= 0 ) { + return __( 'Width must be greater than zero.', 'woocommerce' ); + } + }, + [ dimensions?.width ] + ); + + const { + ref: dimensionsLengthRef, + error: dimensionsLengthValidationError, + validate: validateDimensionsLength, + } = useValidation< Product >( + `dimensions_length-${ clientId }`, + async function dimensionsLengthValidator() { + if ( dimensions?.length && +dimensions.length <= 0 ) { + return __( 'Length must be greater than zero.', 'woocommerce' ); + } + }, + [ dimensions?.length ] + ); + + const { + ref: dimensionsHeightRef, + error: dimensionsHeightValidationError, + validate: validateDimensionsHeight, + } = useValidation< Product >( + `dimensions_height-${ clientId }`, + async function dimensionsHeightValidator() { + if ( dimensions?.height && +dimensions.height <= 0 ) { + return __( 'Height must be greater than zero.', 'woocommerce' ); + } + }, + [ dimensions?.height ] + ); + + const { + ref: weightRef, + error: weightValidationError, + validate: validateWeight, + } = useValidation< Product >( + `weight-${ clientId }`, + async function weightValidator() { + if ( weight && +weight <= 0 ) { + return __( 'Weight must be greater than zero.', 'woocommerce' ); + } + }, + [ weight ] + ); + const dimensionsWidthProps = { ...getDimensionsControlProps( 'width', 'A' ), id: useInstanceId( BaseControl, `product_shipping_dimensions_width` ) as string, + ref: dimensionsWidthRef, + onBlur: validateDimensionsWidth, }; const dimensionsLengthProps = { ...getDimensionsControlProps( 'length', 'B' ), @@ -92,6 +156,8 @@ export function Edit( {}: BlockEditProps< ShippingDimensionsBlockAttributes > ) BaseControl, `product_shipping_dimensions_length` ) as string, + ref: dimensionsLengthRef, + onBlur: validateDimensionsLength, }; const dimensionsHeightProps = { ...getDimensionsControlProps( 'height', 'C' ), @@ -99,6 +165,8 @@ export function Edit( {}: BlockEditProps< ShippingDimensionsBlockAttributes > ) BaseControl, `product_shipping_dimensions_height` ) as string, + ref: dimensionsHeightRef, + onBlur: validateDimensionsHeight, }; const weightProps = { id: useInstanceId( BaseControl, `product_shipping_weight` ) as string, @@ -106,41 +174,10 @@ export function Edit( {}: BlockEditProps< ShippingDimensionsBlockAttributes > ) value: formatNumber( String( weight ) ), onChange: ( value: string ) => setWeight( parseNumber( value ) ), suffix: weightUnit, + ref: weightRef, + onBlur: validateWeight, }; - const dimensionsWidthValidationError = useValidation( - 'product/dimensions/width', - function dimensionsWidthValidator() { - if ( dimensions?.width && +dimensions.width <= 0 ) { - return __( 'Width must be greater than zero.', 'woocommerce' ); - } - } - ); - const dimensionsLengthValidationError = useValidation( - 'product/dimensions/length', - function dimensionsLengthValidator() { - if ( dimensions?.length && +dimensions.length <= 0 ) { - return __( 'Length must be greater than zero.', 'woocommerce' ); - } - } - ); - const dimensionsHeightValidationError = useValidation( - 'product/dimensions/height', - function dimensionsHeightValidator() { - if ( dimensions?.height && +dimensions.height <= 0 ) { - return __( 'Height must be greater than zero.', 'woocommerce' ); - } - } - ); - const weightValidationError = useValidation( - 'product/weight', - function weightValidator() { - if ( weight && +weight <= 0 ) { - return __( 'Weight must be greater than zero.', 'woocommerce' ); - } - } - ); - return (

{ __( 'Dimensions', 'woocommerce' ) }

@@ -205,6 +242,17 @@ export function Edit( {}: BlockEditProps< ShippingDimensionsBlockAttributes > )
diff --git a/packages/js/product-editor/src/blocks/shipping-dimensions/editor.scss b/packages/js/product-editor/src/blocks/shipping-dimensions/editor.scss index 38bd27dc95e..668f03a6f9e 100644 --- a/packages/js/product-editor/src/blocks/shipping-dimensions/editor.scss +++ b/packages/js/product-editor/src/blocks/shipping-dimensions/editor.scss @@ -18,5 +18,6 @@ width: 100%; height: 100%; padding: $gap; + overflow: visible; } } diff --git a/packages/js/product-editor/src/blocks/style.scss b/packages/js/product-editor/src/blocks/style.scss index ea81340017d..87bb061a386 100644 --- a/packages/js/product-editor/src/blocks/style.scss +++ b/packages/js/product-editor/src/blocks/style.scss @@ -1,3 +1,4 @@ +@import 'attributes/editor.scss'; @import 'category/editor.scss'; @import 'checkbox/editor.scss'; @import 'images/editor.scss'; @@ -5,6 +6,8 @@ @import 'inventory-sku/editor.scss'; @import 'name/editor.scss'; @import 'pricing/editor.scss'; +@import 'regular-price/editor.scss'; +@import 'sale-price/editor.scss'; @import 'schedule-sale/editor.scss'; @import 'section/editor.scss'; @import 'shipping-dimensions/editor.scss'; diff --git a/packages/js/product-editor/src/blocks/summary/edit.tsx b/packages/js/product-editor/src/blocks/summary/edit.tsx index f9d07c81c45..802a9898d47 100644 --- a/packages/js/product-editor/src/blocks/summary/edit.tsx +++ b/packages/js/product-editor/src/blocks/summary/edit.tsx @@ -5,8 +5,9 @@ import { __ } from '@wordpress/i18n'; import { createElement } from '@wordpress/element'; import { BlockEditProps } from '@wordpress/blocks'; import { BaseControl } from '@wordpress/components'; +import { useDispatch } from '@wordpress/data'; import { useEntityProp } from '@wordpress/core-data'; -import uniqueId from 'lodash/uniqueId'; +import { useInstanceId } from '@wordpress/compose'; import classNames from 'classnames'; import { // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -14,6 +15,7 @@ import { AlignmentControl, BlockControls, RichText, + store as blockEditorStore, useBlockProps, } from '@wordpress/block-editor'; @@ -32,12 +34,18 @@ export function Edit( { const blockProps = useBlockProps( { style: { direction }, } ); - const id = uniqueId(); + const contentId = useInstanceId( + Edit, + 'wp-block-woocommerce-product-summary-field__content' + ); const [ summary, setSummary ] = useEntityProp< string >( 'postType', 'product', 'short_description' ); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore No types for this exist yet. + const { clearSelectedBlock } = useDispatch( blockEditorStore ); function handleAlignmentChange( value: SummaryAttributes[ 'align' ] ) { setAttributes( { align: value } ); @@ -47,8 +55,21 @@ export function Edit( { setAttributes( { direction: value } ); } + function handleBlur( event: React.FocusEvent< 'p', Element > ) { + const isToolbar = event.relatedTarget?.closest( + '.block-editor-block-contextual-toolbar' + ); + if ( ! isToolbar ) { + clearSelectedBlock(); + } + } + return ( -
+
{ /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */ } { /* @ts-ignore No types for this exist yet. */ } @@ -65,26 +86,29 @@ export function Edit( { - +
+ +
); diff --git a/packages/js/product-editor/src/blocks/summary/editor.scss b/packages/js/product-editor/src/blocks/summary/editor.scss index cca4b4b19b3..a984c0fe6d1 100644 --- a/packages/js/product-editor/src/blocks/summary/editor.scss +++ b/packages/js/product-editor/src/blocks/summary/editor.scss @@ -1,3 +1,10 @@ +.wp-block-woocommerce-product-summary-field-wrapper { + & .wp-block-woocommerce-product-summary-field { + margin-top: 0; + margin-bottom: 0; + } +} + .components-summary-control { width: 100%; min-height: calc($gap-larger * 3); diff --git a/packages/js/product-editor/src/components/attribute-control/attribute-field.scss b/packages/js/product-editor/src/components/attribute-control/attribute-field.scss index cfd62fa466d..9e5244c5cb2 100644 --- a/packages/js/product-editor/src/components/attribute-control/attribute-field.scss +++ b/packages/js/product-editor/src/components/attribute-control/attribute-field.scss @@ -19,17 +19,3 @@ } } - -.wp-block-woocommerce-product-attributes-field { - - .woocommerce-sortable { - padding: 0; - } - - .woocommerce-list-item { - background: none; - border: none; - border-bottom: 1px solid $gray-200; - padding-left: 0; - } -} diff --git a/packages/js/product-editor/src/components/block-editor/block-editor.tsx b/packages/js/product-editor/src/components/block-editor/block-editor.tsx index 2c011c4ed7a..de33904ec3a 100644 --- a/packages/js/product-editor/src/components/block-editor/block-editor.tsx +++ b/packages/js/product-editor/src/components/block-editor/block-editor.tsx @@ -18,8 +18,10 @@ import { BlockTools, EditorSettings, EditorBlockListSettings, - WritingFlow, ObserveTyping, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore No types for this exist yet. + __unstableEditorStyles as EditorStyles, } from '@wordpress/block-editor'; // It doesn't seem to notice the External dependency block whn @ts-ignore is added. // eslint-disable-next-line @woocommerce/dependency-group @@ -101,16 +103,15 @@ export function BlockEditor( { onChange={ onChange } settings={ settings } > +
{ /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */ } { /* @ts-ignore No types for this exist yet. */ } - - - - - + + +
diff --git a/packages/js/product-editor/src/components/block-editor/style.scss b/packages/js/product-editor/src/components/block-editor/style.scss index 1aa58956bef..538eacaea7b 100644 --- a/packages/js/product-editor/src/components/block-editor/style.scss +++ b/packages/js/product-editor/src/components/block-editor/style.scss @@ -41,6 +41,10 @@ margin-left: auto; margin-right: auto; padding-bottom: 128px; + + h4 { + font-size: 16px; + } } .components-input-control { @@ -75,6 +79,7 @@ .wp-block-columns { gap: $gap-large; + margin-bottom: 0; } .wp-block-woocommerce-product-section { @@ -100,9 +105,3 @@ display: none; // use important or increase specificity. } } - -.woocommerce-layout:has( .woocommerce-product-block-editor ) { - .woocommerce-layout__header { - display: none; - } -} diff --git a/packages/js/product-editor/src/components/details-categories-field/category-field.tsx b/packages/js/product-editor/src/components/details-categories-field/category-field.tsx index f9160e19180..67477a53501 100644 --- a/packages/js/product-editor/src/components/details-categories-field/category-field.tsx +++ b/packages/js/product-editor/src/components/details-categories-field/category-field.tsx @@ -55,31 +55,41 @@ function getSelectedWithParents( return selected; } -function mapFromCategoryType( - categories: ProductCategoryNode[] -): TreeItemType[] { - return categories.map( ( val ) => - val.parent - ? { - value: String( val.id ), - label: val.name, - parent: String( val.parent ), - } - : { - value: String( val.id ), - label: val.name, - } - ); +export function mapFromCategoryToTreeItem( + val: ProductCategoryNode +): TreeItemType { + return val.parent + ? { + value: String( val.id ), + label: val.name, + parent: String( val.parent ), + } + : { + value: String( val.id ), + label: val.name, + }; } -function mapToCategoryType( +export function mapFromTreeItemToCategory( + val: TreeItemType +): ProductCategoryNode { + return { + id: +val.value, + name: val.label, + parent: val.parent ? +val.parent : 0, + }; +} + +export function mapFromCategoriesToTreeItems( + categories: ProductCategoryNode[] +): TreeItemType[] { + return categories.map( mapFromCategoryToTreeItem ); +} + +export function mapFromTreeItemsToCategories( categories: TreeItemType[] ): ProductCategoryNode[] { - return categories.map( ( cat ) => ( { - id: +cat.value, - name: cat.label, - parent: cat.parent ? +cat.parent : 0, - } ) ); + return categories.map( mapFromTreeItemToCategory ); } export const CategoryField: React.FC< CategoryFieldProps > = ( { @@ -129,15 +139,15 @@ export const CategoryField: React.FC< CategoryFieldProps > = ( { ) === -1 } items={ getFilteredItemsForSelectTree( - mapFromCategoryType( categoriesSelectList ), + mapFromCategoriesToTreeItems( categoriesSelectList ), searchValue, - mapFromCategoryType( value ) + mapFromCategoriesToTreeItems( value ) ) } - selected={ mapFromCategoryType( value ) } + selected={ mapFromCategoriesToTreeItems( value ) } onSelect={ ( selectedItems ) => { if ( Array.isArray( selectedItems ) ) { const newItems: ProductCategoryNode[] = - mapToCategoryType( + mapFromTreeItemsToCategories( selectedItems.filter( ( { value: selectedItemValue } ) => ! value.some( diff --git a/packages/js/product-editor/src/components/details-categories-field/create-category-modal.scss b/packages/js/product-editor/src/components/details-categories-field/create-category-modal.scss index 4c8ead0e0da..d9e63aa1052 100644 --- a/packages/js/product-editor/src/components/details-categories-field/create-category-modal.scss +++ b/packages/js/product-editor/src/components/details-categories-field/create-category-modal.scss @@ -1,5 +1,6 @@ .woocommerce-create-new-category-modal { min-width: 650px; + overflow: visible; &__buttons { margin-top: $gap-larger; diff --git a/packages/js/product-editor/src/components/details-categories-field/create-category-modal.tsx b/packages/js/product-editor/src/components/details-categories-field/create-category-modal.tsx index eefec178245..f1020c7cc6a 100644 --- a/packages/js/product-editor/src/components/details-categories-field/create-category-modal.tsx +++ b/packages/js/product-editor/src/components/details-categories-field/create-category-modal.tsx @@ -2,7 +2,7 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { Button, Modal, Spinner, TextControl } from '@wordpress/components'; +import { Button, Modal, TextControl } from '@wordpress/components'; import { useDebounce } from '@wordpress/compose'; import { useState, @@ -11,8 +11,8 @@ import { } from '@wordpress/element'; import { useDispatch } from '@wordpress/data'; import { - __experimentalSelectControl as SelectControl, - __experimentalSelectControlMenu as Menu, + __experimentalSelectTreeControl as SelectTree, + TreeItemType as Item, } from '@woocommerce/components'; import { recordEvent } from '@woocommerce/tracks'; import { @@ -24,7 +24,10 @@ import { * Internal dependencies */ import { ProductCategoryNode, useCategorySearch } from './use-category-search'; -import { CategoryFieldItem } from './category-field-item'; +import { + mapFromCategoriesToTreeItems, + mapFromCategoryToTreeItem, +} from './category-field'; type CreateCategoryModalProps = { initialCategoryName?: string; @@ -32,15 +35,6 @@ type CreateCategoryModalProps = { onCreate: ( newCategory: ProductCategory ) => void; }; -function getCategoryItemLabel( item: ProductCategoryNode | null ): string { - return item?.name || ''; -} -function getCategoryItemValue( - item: ProductCategoryNode | null -): string | number { - return item?.id || ''; -} - export const CreateCategoryModal: React.FC< CreateCategoryModalProps > = ( { initialCategoryName, onCancel, @@ -49,9 +43,8 @@ export const CreateCategoryModal: React.FC< CreateCategoryModalProps > = ( { const { categoriesSelectList, isSearching, - categoryTreeKeyValues, searchCategories, - getFilteredItems, + getFilteredItemsForSelectTree, } = useCategorySearch(); const { createNotice } = useDispatch( 'core/notices' ); const [ isCreating, setIsCreating ] = useState( false ); @@ -63,6 +56,9 @@ export const CreateCategoryModal: React.FC< CreateCategoryModalProps > = ( { const [ categoryParent, setCategoryParent ] = useState< ProductCategoryNode | null >( null ); + const [ categoryParentTypedValue, setCategoryParentTypedValue ] = + useState( '' ); + const onSave = async () => { recordEvent( 'product_category_add', { new_product_page: true, @@ -101,8 +97,7 @@ export const CreateCategoryModal: React.FC< CreateCategoryModalProps > = ( { value={ categoryName } onChange={ setCategoryName } /> - - items={ categoriesSelectList } + ', 'woocommerce' ), { @@ -113,78 +108,34 @@ export const CreateCategoryModal: React.FC< CreateCategoryModalProps > = ( { ), } ) } - selected={ categoryParent } - onSelect={ ( item ) => item && setCategoryParent( item ) } + id="parent-category-field" + isLoading={ isSearching } + items={ getFilteredItemsForSelectTree( + mapFromCategoriesToTreeItems( categoriesSelectList ), + categoryParentTypedValue, + [] + ) } + shouldNotRecursivelySelect + selected={ + categoryParent + ? mapFromCategoryToTreeItem( categoryParent ) + : undefined + } + onSelect={ ( item: Item ) => + item && + setCategoryParent( { + id: +item.value, + name: item.label, + parent: item.parent ? +item.parent : 0, + } ) + } onRemove={ () => setCategoryParent( null ) } - onInputChange={ debouncedSearch } - getFilteredItems={ getFilteredItems } - getItemLabel={ getCategoryItemLabel } - getItemValue={ getCategoryItemValue } - > - { ( { - items, - isOpen, - getMenuProps, - highlightedIndex, - getItemProps, - } ) => { - return ( - - { [ - isSearching ? ( -
-
- -
-
- ) : null, - ...items - .filter( - ( item ) => - categoryTreeKeyValues[ item.id ] - ?.parentID === 0 - ) - .map( ( item ) => { - return ( - - ); - } ), - ].filter( - ( item ): item is JSX.Element => - item !== null - ) } -
- ); + onInputChange={ ( value ) => { + debouncedSearch( value ); + setCategoryParentTypedValue( value || '' ); } } - + createValue={ categoryParentTypedValue } + />