diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 7eefddc..42ab917 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -1,9 +1,14 @@
name: Tests
+env:
+ BUILD_CLDR_VERSION: "47"
+
on:
push:
branches:
- main
+ tags-ignore:
+ - "**"
pull_request:
branches:
- main
@@ -13,18 +18,21 @@ jobs:
name: Check PHP coding style
runs-on: ubuntu-latest
steps:
- - name: Setup PHP
+ -
+ name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.1"
extensions: mbstring, zip
tools: composer:2, php-cs-fixer:3
coverage: none
- - name: Checkout
- uses: actions/checkout@v2
- - name: Check PHP coding style
- run: |
- php-cs-fixer fix --path-mode=intersection --config=./.php-cs-fixer.dist.php --dry-run --using-cache=no --diff --show-progress=dots --verbose --no-interaction --ansi .
+ -
+ name: Checkout
+ uses: actions/checkout@v4
+ -
+ name: Check PHP coding style
+ run: php-cs-fixer fix --config=./.php-cs-fixer.dist.php --dry-run --using-cache=no --diff --show-progress=dots --verbose --no-interaction --ansi
+
phpunit:
name: Run PHPUnit tests
needs: php-coding-style
@@ -45,24 +53,71 @@ jobs:
- "8.0"
- "8.1"
- "8.2"
+ - "8.3"
+ - "8.4"
include:
- os: windows-latest
php-version: "5.6"
- os: windows-latest
php-version: "7.4"
- os: windows-latest
- php-version: "8.1"
+ php-version: "8.4"
runs-on: ${{ matrix.os }}
steps:
- - name: Setup PHP
+ -
+ name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
tools: composer:v2
coverage: none
- - name: Checkout
- uses: actions/checkout@v2
- - name: Install Composer dependencies
+ -
+ name: Checkout
+ uses: actions/checkout@v4
+ -
+ name: Install Composer dependencies
run: composer update --no-progress --no-suggest --optimize-autoloader --ansi --no-interaction
- - name: Run PHPUnit
+ -
+ name: Build CLDR data
+ run: php ./bin/import-cldr-data ${{ env.BUILD_CLDR_VERSION }}
+ -
+ name: Run PHPUnit
run: composer --no-interaction run-script test
+
+ commit:
+ name: Commit CLDR data
+ runs-on: ubuntu-latest
+ needs: phpunit
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+ steps:
+ -
+ name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: 8.4
+ tools: none
+ coverage: none
+ -
+ name: Checkout
+ uses: actions/checkout@v4
+ -
+ name: Build CLDR data
+ run: php ./bin/import-cldr-data ${{ env.BUILD_CLDR_VERSION }}
+ -
+ name: Check changes
+ id: check-changes
+ run: |
+ git add --all src/cldr-data
+ if git diff-index --name-status --exit-code HEAD src/cldr-data; then
+ echo 'No changes detected.'
+ else
+ echo 'Changes detected.'
+ echo 'commit=yes' >> $GITHUB_OUTPUT
+ fi
+ - name: Commit changes
+ if: steps.check-changes.outputs.commit == 'yes'
+ run: |
+ git config --local user.name 'GitHub Actions'
+ git config --local user.email noreply@github.com
+ git commit -m 'Update CLDR data'
+ git push
diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php
index ee45cc4..45c4dfa 100644
--- a/.php-cs-fixer.dist.php
+++ b/.php-cs-fixer.dist.php
@@ -26,6 +26,7 @@
])
->append([
__DIR__ . '/bin/export-plural-rules',
+ __DIR__ . '/bin/import-cldr-data',
])
->in(__DIR__)
)
@@ -245,8 +246,6 @@
'phpdoc_no_access' => true,
// No alias PHPDoc tags should be used.
'phpdoc_no_alias_tag' => true,
- // `@return void` and `@return null` annotations should be omitted from PHPDoc.
- 'phpdoc_no_empty_return' => true,
// `@package` and `@subpackage` annotations should be omitted from PHPDoc.
'phpdoc_no_package' => true,
// Classy that does not inherit must not have `@inheritdoc` tags.
diff --git a/README.md b/README.md
index 63dcb8e..59f5a17 100644
--- a/README.md
+++ b/README.md
@@ -172,63 +172,7 @@ the resulting formulas will be in this format:
### Generating the CLDR data
This repository uses the CLDR data, including American English (`en_US`) json files.
-In order to generate this data, you can use Docker.
-Start a new Docker container by running
-
-```sh
-docker run --rm -it -v path/to/src/cldr-data:/output alpine:3.13 sh
-```
-
-Then run the following script, setting the values of the variables accordingly to your needs:
-
-```sh
-# The value of the CLDR version (eg 39, 38.1, ...)
-CLDR_VERSION=39
-# Your GitHub username (required since CLDR 38) - see http://cldr.unicode.org/development/maven#TOC-Introduction
-GITHUB_USERNAME=
-# Your GitHub personal access token (required since CLDR 38) - see http://cldr.unicode.org/development/maven#TOC-Introduction
-GITHUB_TOKEN=
-
-if ! test -d /output; then
- echo 'Missing output directory' >&2
- return 1
-fi
-apk -U upgrade
-apk add --no-cache git git-lfs openjdk8 apache-ant maven
-CLDR_MAJORVERSION="$(printf '%s' "$CLDR_VERSION" | sed -E 's/^([0-9]+).*/\1/')"
-SOURCE_DIR="$(mktemp -d)"
-DESTINATION_DIR="$(mktemp -d)"
-git clone --single-branch --depth=1 "--branch=release-$(printf '%s' "$CLDR_VERSION" | tr '.' '-')" https://github.com/unicode-org/cldr.git "$SOURCE_DIR"
-if test $CLDR_MAJORVERSION -lt 38; then
- git -C "$SOURCE_DIR" lfs pull --include tools/java || true
- ant -f "$SOURCE_DIR/tools/java/build.xml" jar
- JARFILE="$SOURCE_DIR/tools/java/cldr.jar"
- DESTINATION_DIR_LOCALE="$DESTINATION_DIR/en_US"
- DESTINATION_FILE_PLURALS="$DESTINATION_DIR/supplemental/plurals.json"
-else
- if test -z "${GITHUB_USERNAME:-}"; then
- echo 'GITHUB_USERNAME is missing' >&2
- return 1
- fi
- if test -z "${GITHUB_TOKEN:-}"; then
- echo 'GITHUB_TOKEN is missing' >&2
- return 1
- fi
- printf 'githubicu%s%s' "$GITHUB_USERNAME" "$GITHUB_TOKEN" > "$SOURCE_DIR/mvn-settings.xml"
- mvn --settings "$SOURCE_DIR/mvn-settings.xml" package -DskipTests=true --file "$SOURCE_DIR/tools/cldr-code/pom.xml"
- JARFILE="$SOURCE_DIR//tools/cldr-code/target/cldr-code.jar"
- DESTINATION_DIR_LOCALE="$DESTINATION_DIR"
- DESTINATION_FILE_PLURALS="$DESTINATION_DIR/supplemental/plurals/plurals.json"
-fi
-java -Duser.language=en -Duser.country=US "-DCLDR_DIR=$SOURCE_DIR" "-DCLDR_GEN_DIR=$DESTINATION_DIR_LOCALE" -jar "$JARFILE" ldml2json -t main -r true -s contributed -m en_US
-java -Duser.language=en -Duser.country=US "-DCLDR_DIR=$SOURCE_DIR" "-DCLDR_GEN_DIR=$DESTINATION_DIR/supplemental" -jar "$JARFILE" ldml2json -s contributed -o true -t supplemental
-mkdir -p /output/main/en-US
-cp $DESTINATION_DIR/en_US/languages.json /output/main/en-US/
-cp $DESTINATION_DIR/en_US/scripts.json /output/main/en-US/
-cp $DESTINATION_DIR/en_US/territories.json /output/main/en-US/
-mkdir -p /output/supplemental
-cp "$DESTINATION_FILE_PLURALS" /output/supplemental/
-```
+In order to generate this data, you can use the `bin/import-cldr-data` CLI command.
## Support this project
diff --git a/bin/import-cldr-data b/bin/import-cldr-data
new file mode 100755
index 0000000..db6d2fa
--- /dev/null
+++ b/bin/import-cldr-data
@@ -0,0 +1,656 @@
+#!/usr/bin/env php
+outputDir) && !mkdir($options->outputDir, 0777, true)) {
+ throw new RuntimeException("Cannot create output directory: {$options->outputDir}\n");
+ }
+ $options->outputDir = str_replace(DIRECTORY_SEPARATOR, '/', realpath($options->outputDir));
+ $documentStorage = new DocumentStorage($options);
+ echo 'Processing languages... ';
+ $languages = new Languages($options, $documentStorage);
+ echo "done.\n";
+ echo 'Processing scripts... ';
+ $scripts = new Scripts($options, $documentStorage);
+ echo "done.\n";
+ echo 'Processing territories... ';
+ $territories = new Territories($options, $documentStorage);
+ echo "done.\n";
+ echo 'Processing plural rules... ';
+ $plurals = new Plurals($options, $documentStorage, $languages);
+ echo "done.\n";
+ echo 'Saving... ';
+
+ $languages->save();
+ $scripts->save();
+ $territories->save();
+ $plurals->save();
+
+ echo "done.\n";
+}
+
+class Options
+{
+ /**
+ * @var string
+ */
+ public $cldrVersion;
+
+ /**
+ * @var string
+ */
+ public $outputDir;
+
+ public function __construct(array $argv)
+ {
+ if (array_intersect($argv, array('-h', '--help'))) {
+ $this->showSyntax($argv[0], 0);
+ }
+ $this->outputDir = $this->getDefaultOutputDir();
+ switch (count($argv)) {
+ case 3:
+ $this->outputDir = str_replace(DIRECTORY_SEPARATOR, '/', $argv[2]);
+ // no break
+ case 2:
+ $this->cldrVersion = $argv[1];
+ if (!preg_match('/^\d+(\.\d+)?(-(alpha|beta)\d+)?$/', $this->cldrVersion)) {
+ throw new RuntimeException("{$this->cldrVersion} is not a valid CLDR version identifier");
+ }
+ break;
+ default:
+ $this->showSyntax($argv[0], 1);
+ }
+ }
+
+ /**
+ * @param string $programName
+ * @param int $exitCode
+ *
+ * @return never
+ */
+ private function showSyntax($programName, $exitCode)
+ {
+ $programName = str_replace('/', DIRECTORY_SEPARATOR, $programName);
+ $defaultOutputDir = str_replace('/', DIRECTORY_SEPARATOR, $this->getDefaultOutputDir());
+
+ echo << [output-dir]
+
+Arguments:
+ cldr-version: the version of the CLDR data.
+ Examples:
+ 47
+ 47-beta2
+ 47-alpha1
+ 46.1
+ 46.1-beta1
+ output-dir: the directory where the data will be written to
+ Default: {$defaultOutputDir}
+
+EOT;
+ exit($exitCode);
+ }
+
+ /**
+ * @return string
+ */
+ private function getDefaultOutputDir()
+ {
+ return str_replace(DIRECTORY_SEPARATOR, '/', dirname(__DIR__)) . '/src/cldr-data';
+ }
+}
+
+class DocumentStorage
+{
+ /**
+ * @var string
+ */
+ private $baseUrl;
+
+ private $context;
+
+ private $cache;
+
+ public function __construct(Options $options)
+ {
+ $this->baseUrl = 'https://raw.githubusercontent.com/unicode-org/cldr/refs/tags/release-' . str_replace('.', '-', $options->cldrVersion);
+ $this->context = stream_context_create(array(
+ 'http' => array(
+ 'follow_location' => 1,
+ 'ignore_errors' => false,
+ ),
+ ));
+ $this->cache = array();
+ }
+
+ /**
+ * @param string $path
+ *
+ * @throws RuntimeException
+ *
+ * @return DOMDocument
+ */
+ public function get($path)
+ {
+ if (!isset($this->cache[$path])) {
+ $xml = $this->fetch($path);
+ $doc = $this->loadXml($xml);
+ $this->cache[$path] = $doc;
+ }
+
+ return $this->cache[$path];
+ }
+
+ /**
+ * @param string $path
+ *
+ * @throws RuntimeException
+ *
+ * @return string
+ */
+ private function fetch($path)
+ {
+ $url = $this->baseUrl . '/' . ltrim($path, '/');
+ set_error_handler(function () {}, -1);
+ $content = file_get_contents($url, false, $this->context);
+ restore_error_handler();
+ if ($content === false) {
+ $details = '';
+ /** @var array $http_response_header */
+ if (!empty($http_response_header)) {
+ $details = " - {$http_response_header[0]}";
+ }
+ throw new RuntimeException("Failed to download from {$url}{$details}");
+ }
+
+ return $content;
+ }
+
+ /**
+ * @param string $xml
+ *
+ * @throws RuntimeException
+ *
+ * @return DOMDocument
+ */
+ private function loadXml($xml)
+ {
+ $doc = new DOMDocument();
+ libxml_clear_errors();
+ $restore = libxml_use_internal_errors(true);
+ $loaded = $doc->loadXML($xml);
+ $errors = libxml_get_errors();
+ libxml_use_internal_errors($restore);
+ $lines = array();
+ foreach ($errors as $error) {
+ $lines[] = "{$error->message} at line {$error->line}";
+ }
+ if (!$loaded || $errors !== array()) {
+ throw new RuntimeException("Failed to parse XML:\n" . implode("\n", $lines));
+ }
+
+ return $doc;
+ }
+}
+
+abstract class Processor
+{
+ /**
+ * @var Options
+ */
+ protected $options;
+
+ /**
+ * @var array
+ */
+ protected $data;
+
+ /**
+ * @var DocumentStorage
+ */
+ private $documentStorage;
+
+ /**
+ * @var string
+ */
+ private $path;
+
+ protected function __construct(Options $options, DocumentStorage $documentStorage, $path)
+ {
+ $this->options = $options;
+ $this->documentStorage = $documentStorage;
+ $this->path = ltrim($path, '/');
+ $doc = $this->documentStorage->get($this->path);
+ $this->data = $this->parse($doc);
+ }
+
+ /**
+ * @return void
+ */
+ public function save()
+ {
+ $file = $this->getOutputFile();
+ $dir = dirname($file);
+ if (!is_dir($dir) && !mkdir($dir, 0777, true)) {
+ throw new RuntimeException("Cannot create directory: {$dir}");
+ }
+ $flags = 0;
+ if (defined('JSON_UNESCAPED_SLASHES')) {
+ $flags |= JSON_UNESCAPED_SLASHES;
+ }
+ if (defined('JSON_UNESCAPED_UNICODE')) {
+ $flags |= JSON_UNESCAPED_UNICODE;
+ }
+ if (defined('JSON_PRETTY_PRINT')) {
+ $flags |= JSON_PRETTY_PRINT;
+ }
+ if (defined('JSON_THROW_ON_ERROR')) {
+ $flags |= JSON_THROW_ON_ERROR;
+ }
+ $json = json_encode($this->data, $flags);
+ if (!file_put_contents($file, $json)) {
+ throw new RuntimeException("Failed to write to file: {$file}");
+ }
+ }
+
+ /**
+ * @return array
+ */
+ abstract protected function parse(DOMDocument $doc);
+
+ /**
+ * @return void
+ */
+ protected function sortByKeyWithPossiblyAlt(array &$data)
+ {
+ uksort($data, function ($a, $b) {
+ $aAlt = strpos($a, '-alt-') !== false;
+ $bAlt = strpos($b, '-alt-') !== false;
+ if ($aAlt !== $bAlt) {
+ if (strpos("{$a}-alt-", $b) === 0) {
+ return 0;
+ }
+ if (strpos($a, "{$b}-alt-") === 0) {
+ return -1;
+ }
+ }
+
+ return strcasecmp($a, $b);
+ });
+ }
+
+ /**
+ * @return string
+ */
+ abstract protected function getOutputRelativeFileName();
+
+ /**
+ * @param string $xml
+ *
+ * @return DOMDocument
+ */
+ private function loadXml($xml)
+ {
+ $doc = new DOMDocument();
+ libxml_clear_errors();
+ $restore = libxml_use_internal_errors(true);
+ $loaded = $doc->loadXML($xml);
+ $errors = libxml_get_errors();
+ libxml_use_internal_errors($restore);
+ $lines = array();
+ foreach ($errors as $error) {
+ $lines[] = "{$error->message} at line {$error->line}";
+ }
+ if (!$loaded || $errors !== array()) {
+ throw new RuntimeException("Failed to parse XML:\n" . implode("\n", $lines));
+ }
+ return $doc;
+ }
+
+ /**
+ * @return string
+ */
+ private function getOutputFile()
+ {
+ return $this->options->outputDir . '/' . ltrim($this->getOutputRelativeFileName(), '/');
+ }
+}
+
+class Plurals extends Processor
+{
+ /**
+ * @var Languages
+ */
+ private $languages;
+
+ public function __construct(Options $options, DocumentStorage $documentStorage, Languages $languages)
+ {
+ $this->languages = $languages;
+ parent::__construct($options, $documentStorage, 'common/supplemental/plurals.xml');
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see Processor::parse()
+ */
+ protected function parse(DOMDocument $doc)
+ {
+ $data = array();
+ $xpath = new DOMXPath($doc);
+ $xPluralRulesList = $xpath->query('/supplementalData/plurals[@type="cardinal"]/pluralRules');
+ $definedLanguageIDs = $this->languages->getDefinedLanguageIDs();
+ $knownMissingLanguages = array(
+ 'guw', // Gun
+ 'lld', // Dolomitic Ladin
+ 'hnj', // Hmong Njua
+ 'nah', // Nahuatl
+ 'smi', // Sami
+ );
+ $replacements = array(
+ 'in' => 'id', // Former Indonesian
+ 'iw' => 'he', // Former Hebrew
+ 'jw' => 'jv', // Former Javanese
+ 'ji' => 'yi', // Former Yiddish
+ 'mo' => 'ro-MD', // former Moldavian
+ 'bh' => '', // Former Bihari: dismissed because it can be 'bho', 'mai' or 'mag'
+ // Just a CLDR placeholder
+ 'root' => '',
+ );
+ $unrecognizedLocaleCodes = array();
+ foreach ($xPluralRulesList as $xPluralRules) {
+ $locales = preg_split('/\s+/', (string) $xPluralRules->getAttribute('locales'), -1, PREG_SPLIT_NO_EMPTY);
+ if ($locales === array()) {
+ throw new RuntimeException('No locales found in pluralRules element');
+ }
+ $elements = array(
+ 'pluralRule-count-zero' => null,
+ 'pluralRule-count-one' => null,
+ 'pluralRule-count-two' => null,
+ 'pluralRule-count-few' => null,
+ 'pluralRule-count-many' => null,
+ 'pluralRule-count-other' => null,
+ );
+ foreach ($xPluralRules->childNodes as $xPluralRule) {
+ if (!$xPluralRule instanceof DOMElement) {
+ continue;
+ }
+ if ($xPluralRule->tagName !== 'pluralRule') {
+ throw new RuntimeException("Unexpected element: {$xPluralRule->tagName}");
+ }
+ $count = (string) $xPluralRule->getAttribute('count');
+ if ($count === '') {
+ throw new RuntimeException('Missing count attribute');
+ }
+ $key = "pluralRule-count-{$count}";
+ if (!array_key_exists($key, $elements)) {
+ throw new RuntimeException("Unknown count: {$count}");
+ }
+ if ($elements[$key] !== null) {
+ throw new RuntimeException("Duplicate count: {$count}");
+ }
+ $elements[$key] = $xPluralRule->textContent;
+ }
+ $elements = array_filter($elements, function ($value) {
+ return $value !== null;
+ });
+ if ($elements === array()) {
+ throw new RuntimeException('No plural rules found');
+ }
+ foreach ($locales as $locale) {
+ $locale = str_replace('_', '-', $locale);
+ $overwrite = true;
+ if (isset($data[$locale]) && array_search($locale, $replacements, true) === false) {
+ throw new RuntimeException("Duplicate locale: {$locale}");
+ }
+ if (!in_array($locale, $definedLanguageIDs, true) && !in_array($locale, $knownMissingLanguages, true)) {
+ if (!isset($replacements[$locale])) {
+ $unrecognizedLocaleCodes[] = $locale;
+ continue;
+ }
+ $locale = $replacements[$locale];
+ if ($locale === '') {
+ continue;
+ }
+ $overwrite = false;
+ }
+ if ($overwrite || !isset($data[$locale])) {
+ $data[$locale] = $elements;
+ }
+ }
+ }
+ if ($unrecognizedLocaleCodes !== array()) {
+ throw new RuntimeException("The following locales are not defined:\n- " . implode("\n- ", $unrecognizedLocaleCodes));
+ }
+ if ($data === array()) {
+ throw new RuntimeException('No plural rules found');
+ }
+ $this->sortByKeyWithPossiblyAlt($data);
+
+ return array(
+ 'supplemental' => array(
+ 'version' => array(
+ '_cldrVersion' => $this->options->cldrVersion,
+ ),
+ 'plurals-type-cardinal' => $data,
+ ),
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see Processor::getOutputRelativeFileName()
+ */
+ protected function getOutputRelativeFileName()
+ {
+ return 'supplemental/plurals.json';
+ }
+}
+
+abstract class LocaleDisplayName extends Processor
+{
+ public function __construct(Options $options, DocumentStorage $documentStorage)
+ {
+ parent::__construct($options, $documentStorage, 'common/main/en.xml');
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see Processor::parse()
+ */
+ protected function parse(DOMDocument $doc)
+ {
+ $data = array();
+ $xpath = new DOMXPath($doc);
+ $xElementList = $xpath->query($this->getXPathSelector());
+ foreach ($xElementList as $xElement) {
+ $type = (string) $xElement->getAttribute('type');
+ if ($type === '') {
+ throw new RuntimeException('Missing type attribute');
+ }
+ $key = str_replace('_', '-', $type);
+ $alt = (string) $xElement->getAttribute('alt');
+ if ($alt !== '') {
+ $key = "{$key}-alt-{$alt}";
+ }
+ if (isset($data[$key])) {
+ throw new RuntimeException("Duplicate key: {$key}");
+ }
+ $data[$key] = (string) $xElement->textContent;
+ }
+ if ($data === array()) {
+ throw new RuntimeException('No elements found');
+ }
+ $this->sortByKeyWithPossiblyAlt($data);
+
+ return array(
+ 'main' => array(
+ 'en-US' => array(
+ 'identity' => array(
+ 'version' => array(
+ '_cldrVersion' => $this->options->cldrVersion,
+ ),
+ 'language' => 'en',
+ 'territory' => 'US',
+ ),
+ 'localeDisplayNames' => array(
+ $this->getExportedNodeName() => $data,
+ ),
+ ),
+ ),
+ );
+ }
+
+ /**
+ * @return string
+ */
+ abstract protected function getXPathSelector();
+
+ /**
+ * @return string
+ */
+ abstract protected function getExportedNodeName();
+}
+
+class Languages extends LocaleDisplayName
+{
+ /**
+ * @return string[]
+ */
+ public function getDefinedLanguageIDs()
+ {
+ return array_values(array_filter(
+ array_keys($this->data['main']['en-US']['localeDisplayNames'][$this->getExportedNodeName()]),
+ function ($key) {
+ return strpos((string) $key, '-alt-') === false;
+ }
+ ));
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see LocaleDisplayName::getXPathSelector()
+ */
+ protected function getXPathSelector()
+ {
+ return '/ldml/localeDisplayNames/languages/language';
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see LocaleDisplayName::getExportedNodeName()
+ */
+ protected function getExportedNodeName()
+ {
+ return 'languages';
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see Processor::getOutputRelativeFileName()
+ */
+ protected function getOutputRelativeFileName()
+ {
+ return 'main/en-US/languages.json';
+ }
+}
+
+class Scripts extends LocaleDisplayName
+{
+ /**
+ * {@inheritdoc}
+ *
+ * @see LocaleDisplayName::getXPathSelector()
+ */
+ protected function getXPathSelector()
+ {
+ return '/ldml/localeDisplayNames/scripts/script';
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see LocaleDisplayName::getExportedNodeName()
+ */
+ protected function getExportedNodeName()
+ {
+ return 'scripts';
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see Processor::getOutputRelativeFileName()
+ */
+ protected function getOutputRelativeFileName()
+ {
+ return 'main/en-US/scripts.json';
+ }
+}
+
+class Territories extends LocaleDisplayName
+{
+ /**
+ * {@inheritdoc}
+ *
+ * @see LocaleDisplayName::getXPathSelector()
+ */
+ protected function getXPathSelector()
+ {
+ return '/ldml/localeDisplayNames/territories/territory';
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see LocaleDisplayName::getExportedNodeName()
+ */
+ protected function getExportedNodeName()
+ {
+ return 'territories';
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @see Processor::getOutputRelativeFileName()
+ */
+ protected function getOutputRelativeFileName()
+ {
+ return 'main/en-US/territories.json';
+ }
+}
+
+try {
+ main($argv);
+} catch (RuntimeException $e) {
+ fwrite(STDERR, $e->getMessage() . "\n");
+ exit(1);
+}
diff --git a/bin/import-cldr-data.bat b/bin/import-cldr-data.bat
new file mode 100644
index 0000000..b99ef8d
--- /dev/null
+++ b/bin/import-cldr-data.bat
@@ -0,0 +1 @@
+@php "%~dpn0" %*
\ No newline at end of file
diff --git a/composer.json b/composer.json
index 2f914a4..6969ccc 100644
--- a/composer.json
+++ b/composer.json
@@ -46,6 +46,7 @@
"test": "phpunit"
},
"bin": [
- "bin/export-plural-rules"
+ "bin/export-plural-rules",
+ "bin/import-cldr-data"
]
}
\ No newline at end of file
diff --git a/src/CldrData.php b/src/CldrData.php
index 59503a9..cdaebc2 100644
--- a/src/CldrData.php
+++ b/src/CldrData.php
@@ -306,6 +306,8 @@ private static function getData($key)
);
$knownMissingLanguages = array(
'guw' => 'Gun',
+ 'hnj' => 'Hmong Njua',
+ 'lld' => 'Dolomitic Ladin',
'nah' => 'Nahuatl',
'smi' => 'Sami',
);
diff --git a/tests/test/ExecutableFilesTest.php b/tests/test/ExecutableFilesTest.php
index edf9200..09d51a2 100644
--- a/tests/test/ExecutableFilesTest.php
+++ b/tests/test/ExecutableFilesTest.php
@@ -11,6 +11,7 @@ public function testExecutableFiles()
}
$expected = array(
'bin/export-plural-rules',
+ 'bin/import-cldr-data',
);
$actual = $this->listExecutableFiles();
$this->assertSame($expected, $actual);