diff --git a/build.properties.default b/build.properties.default
new file mode 100644
index 00000000..fbd41f73
--- /dev/null
+++ b/build.properties.default
@@ -0,0 +1,6 @@
+getlocalization.framework.project = sapphire
+getlocalization.framework.user = silverstripe
+getlocalization.framework.password =
+getlocalization.cms.project = silverstripe_cms
+getlocalization.cms.user = silverstripe
+getlocalization.cms.password =
\ No newline at end of file
diff --git a/build.xml b/build.xml
index 326a1d68..1950b7b9 100644
--- a/build.xml
+++ b/build.xml
@@ -16,6 +16,7 @@ phing help
+
@@ -28,22 +29,19 @@ phing help
+
+
SilverStripe Project Build
------------------------------------
This build file contains targets to assist in creating new SilverStripe builds and releases.
-
-Important targets:
-
-* archive - Creates a tar.gz or zip file from the current source, removing version control specific files
-* checkout - Switches all working copies to the specified tag or branch
-* tag - Creates a new git tag in all the nested working copies (optionally pushes the created tag)
-* pushtags - Pushes all local tags to their respective origin repositories
-* update_modules - Checks out repositories defined in the 'dependent-modules' file into the current directory
-* add_module - Checks out a module at a specific repository URL
-* changelog - Create a changelog.md file from the repositories specified in the 'changelog-definitions' file
+Run "phing -l" to get a full list of available targets.
Options:
@@ -65,17 +63,16 @@ Options:
-
+
-
-
+
-
-
+
-
-
-
+
-
-
+
-
@@ -108,17 +105,23 @@ Options:
-
+
-
+
+
-
-
+
@@ -131,7 +134,7 @@ Options:
-
+
Push local tags to origin?
@@ -142,13 +145,15 @@ Options:
-
-
-
+
+
-
-
+
@@ -179,7 +184,7 @@ Options:
-
+
@@ -325,7 +330,8 @@ Options:
-
+
Please choose a version
@@ -350,7 +356,8 @@ Your friendly automated release script.
-
+
@@ -358,14 +365,13 @@ Your friendly automated release script.
-
-
+
-
-
+
@@ -391,10 +397,77 @@ Your friendly automated release script.
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tools/UpdateTranslationsTask.php b/tools/UpdateTranslationsTask.php
new file mode 100644
index 00000000..3420b0ed
--- /dev/null
+++ b/tools/UpdateTranslationsTask.php
@@ -0,0 +1,227 @@
+ 'lang',
+ 'js' => 'javascript/lang'
+ );
+
+ /**
+ * @var String If set, will use existing files rather than try to download them.
+ */
+ protected $downloadPath;
+
+ public function getGlUser() {
+ return $this->glUser;
+ }
+
+ public function setGlUser($newGlUser) {
+ $this->glUser = $newGlUser;
+ return $this;
+ }
+
+ public function getGlPassword() {
+ return $this->glPassword;
+ }
+
+ public function setGlPassword($newGlPassword) {
+ $this->glPassword = $newGlPassword;
+ return $this;
+ }
+
+ public function setModulePath($path) {
+ $this->modulePath = $path;
+ }
+
+ public function setDownloadPath($path) {
+ $this->downloadPath = $path;
+ }
+
+ public function setGlProductName($name) {
+ $this->glProductName = $name;
+ }
+
+ public function main() {
+ if (!is_dir($this->modulePath)) {
+ throw new BuildException("Invalid target directory: $this->modulePath");
+ }
+
+ $downloadPath = $this->downloadPath ? $this->downloadPath : $this->download();
+ $files = $this->findFiles($downloadPath);
+ foreach($files as $file) {
+ $ext = pathinfo($file, PATHINFO_EXTENSION);
+ if($ext == 'yml') {
+ $this->processYmlFile($file);
+ } elseif($ext == 'js') {
+ $this->processJavascriptFile($file);
+ } else {
+ throw new LogicException(sprintf('Unknown extension: %s', $ext));
+ }
+ }
+
+ }
+
+ /**
+ * @return File path to a folder structure containing translation files
+ */
+ protected function download() {
+ $tmpFolder = tempnam(sys_get_temp_dir(), $this->glProductName . '-');
+ $tmpFilePath = $tmpFolder . '.zip';
+ rename($tmpFolder, $tmpFilePath);
+ $url = sprintf(self::$url_translations, $this->glProductName);
+
+ $this->log(sprintf("Downloading $url to $tmpFilePath"));
+
+ $ch = curl_init($url);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_USERPWD, $this->glUser. ":" . $this->glPassword);
+ $data = curl_exec($ch);
+ $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
+ if($code >= 400) {
+ throw new BuildException(sprintf(
+ 'Error downloading %s: %s %s',
+ $url,
+ $code,
+ $data
+ ));
+ }
+ if(curl_error($ch)) {
+ throw new BuildException(sprintf(
+ 'Error downloading %s: %s (#%s)',
+ $url,
+ curl_error($ch),
+ curl_errno($ch)
+ ));
+ }
+
+ curl_close($ch);
+ file_put_contents($tmpFilePath, $data);
+
+ $this->log(sprintf("Extracting to $tmpFolder"));
+ $this->exec("unzip $tmpFilePath -d $tmpFolder");
+
+ return $tmpFolder;
+ }
+
+ /**
+ * @param String Absolute path to a folder structure containing translation files
+ * @return Array with file paths
+ */
+ protected function findFiles($path) {
+ // Recursively find files with certain extensions.
+ // Can't use glob() since its non-recursive.
+ // Directory structure doesn't matter here.
+ $files = array();
+ $matches = new RegexIterator(
+ new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator($path)
+ ),
+ '/^.+\.(yml$|js)/i',
+ RecursiveRegexIterator::GET_MATCH
+ );
+ foreach($matches as $match) $files[] = $match[0];
+ return $files;
+ }
+
+ protected function processYmlFile($file) {
+ $this->log(sprintf("Processing $file"));
+
+ // Rename locale to correct convention (underscored rather than dashed).
+ // GL wants filenames to adhere to its saved locales, but SS framework
+ // can't easily change to that format for backwards compat reasons, so we need to convert.
+ // The passed in file name doesn't really matter here, only the contained locale.
+ // By convention, the first line in the YAML file is always the locale used, as a YAML "root key".
+ $content = file_get_contents($file);
+ preg_match('/^([\w-_]*):/', $content, $matches);
+ $locale = $matches[1];
+ $locale = str_replace('-', '_', $locale);
+ $locale = str_replace(':', '', $locale);
+
+ // Convert faulty multiline double quoted string YAML
+ // to block format, in order to allow the YAML Parser to open it later
+ // TODO Remove once getlocalization.com has fixed their output format (see support.getlocalization.com #2022)
+ $isBlock = false;
+ $blockIndex = -1;
+ $lines = explode(PHP_EOL, $content);
+ $keyedLineRegex = '/^\s*[\w\d-_]*:\s*/';
+ $leadingQuoteRegex = '/^\s*\"/';
+ $trailingQuoteRegex = '/[^\\\\]\"$/';
+ foreach($lines as $i => $line) {
+ preg_match($keyedLineRegex, $line, $matches);
+ $key = $matches ? $matches[0] : null;
+ $val = trim(preg_replace($keyedLineRegex, '', $line));
+ // If its a multiline double quoted string (no unescaped closing quote)
+ if($val && $line != '"' && preg_match($leadingQuoteRegex, $val) && !preg_match($trailingQuoteRegex, $val)) {
+ $isBlock = true;
+ $blockIndex = $i;
+ } elseif($key) {
+ $isBlock = false;
+ $blockIndex = -1;
+ } else {
+ $lines[$blockIndex] .= $line;
+ unset($lines[$i]);
+ }
+ }
+ $content = implode(PHP_EOL, $lines);
+
+ // Parse YML as a sanity check,
+ // and reorder alphabetically by key to ensure consistent diffs.
+ require_once '../framework/thirdparty/zend_translate_railsyaml/library/Translate/Adapter/thirdparty/sfYaml/lib/sfYaml.php';
+ require_once '../framework/thirdparty/zend_translate_railsyaml/library/Translate/Adapter/thirdparty/sfYaml/lib/sfYamlParser.php';
+ require_once '../framework/thirdparty/zend_translate_railsyaml/library/Translate/Adapter/thirdparty/sfYaml/lib/sfYamlDumper.php';
+ $yamlHandler = new sfYaml();
+ $yml = $yamlHandler->parse($content);
+ if(isset($yml[$locale]) && is_array($yml[$locale])) {
+ ksort($yml[$locale]);
+ foreach($yml[$locale] as $k => &$v) {
+ if(is_array($v)) ksort($v);
+ }
+ }
+ $content = $yamlHandler->dump($yml, 99); // don't inline first levels
+
+ // Save into correct path, overwriting existing files
+ $path = $this->modulePath . '/' . $this->langFolders['yml'] . '/' . $locale . '.yml';
+ $this->log("Saving to $path");
+ file_put_contents($path, $content);
+ }
+
+ protected function processJavascriptFile($file) {
+ $this->log(sprintf("Processing $file"));
+
+ $locale = pathinfo($file, PATHINFO_FILENAME);
+ $locale = str_replace('-', '_', $locale);
+
+ // Save into correct path, overwriting existing files
+ $path = $this->modulePath . '/' . $this->langFolders['js'] . '/' . $locale . '.yml';
+ $this->log("Saving to $path");
+ file_put_contents($path, file_get_contents($file));
+ }
+}
+
+
+?>
\ No newline at end of file