| 2004 | function process_i18n( $slug, $branch = 'dev', $type = 'all' ) { |
| 2005 | if ( empty( $slug ) ) |
| 2006 | return false; |
| 2007 | |
| 2008 | if ( 'stable' !== $branch ) |
| 2009 | $branch = 'dev'; |
| 2010 | |
| 2011 | if ( 'code' !== $type && 'readme' !== $type ) |
| 2012 | $type = 'all'; |
| 2013 | |
| 2014 | $path_rel = "{$slug}/trunk/"; |
| 2015 | |
| 2016 | if ( 'stable' === $branch ) { |
| 2017 | if ( false == ( $stable_tag = $this->get_stable_tag_dir_using( $path_rel ) ) ) { |
| 2018 | // Can't get a stable tag, bail out |
| 2019 | return false; |
| 2020 | } else if ( 'trunk' == trim( $stable_tag['tag_dir'], '/' ) ) { |
| 2021 | // If stable is trunk, then it's really same as dev, switch to that |
| 2022 | $branch = 'dev'; |
| 2023 | } else { |
| 2024 | // We're dealing with an actual stable tag, go for it |
| 2025 | $path_rel = "{$slug}/{$stable_tag['tag_dir']}"; // |
| 2026 | } |
| 2027 | } |
| 2028 | |
| 2029 | if ( 'code' === $type || 'all' === $type ) |
| 2030 | $this->process_code_i18n( $path_rel, $branch ); |
| 2031 | |
| 2032 | if ( 'readme' === $type || 'all' === $type ) |
| 2033 | $this->process_readme_i18n( $path_rel, $branch ); |
| 2034 | |
| 2035 | echo "Processed {$type} for {$path_rel}\n"; |
| 2036 | return true; |
| 2037 | } |
| 2038 | |
| 2039 | function process_code_i18n( $path_rel, $branch = 'dev' ) { |
| 2040 | if ( false == ( $export_path = $this->create_export( $path_rel ) ) ) |
| 2041 | return false; |
| 2042 | |
| 2043 | $slug = preg_replace( '|^/?([^/]+)/?.+?$|', '\1', $path_rel ); |
| 2044 | |
| 2045 | if ( empty( $slug ) || empty( $export_path ) || !preg_match( '/^[a-z0-9-]+$/i', $slug ) || !is_dir( $export_path ) ) |
| 2046 | return false; |
| 2047 | |
| 2048 | $old_cwd = getcwd(); |
| 2049 | chdir( $export_path ); |
| 2050 | |
| 2051 | // Check for a plugin text domain declaration and loading, grep recursively, not necessarily in [slug].php |
| 2052 | if ( ! shell_exec( 'grep -r --include "*.php" "Text Domain: ' . escapeshellarg( $slug ) . '" .' ) && ! shell_exec( 'grep -r --include "*.php" "\bload_plugin_textdomain\b" .' ) ) |
| 2053 | return false; |
| 2054 | |
| 2055 | if ( !class_exists( 'PotExtMeta' ) ) |
| 2056 | require_once( __DIR__ . '/i18n-tools/pot-ext-meta.php' ); |
| 2057 | |
| 2058 | $pot_file = "./{$slug}.pot"; |
| 2059 | $other_pot_loc = str_replace( './', './languages/', $pot_file ); |
| 2060 | |
| 2061 | if ( file_exists( $other_pot_loc ) ) { // Some plugins have their pot file in /languages/, use it |
| 2062 | $pot_file = $other_pot_loc; |
| 2063 | } else if ( ! file_exists( $pot_file ) ) { // BuddyPress, for EG, has theirs in the root's repo, use it |
| 2064 | $makepot = new MakePOT; |
| 2065 | // Create pot file from code |
| 2066 | if ( ! $makepot->wp_plugin( '.', $pot_file, $slug ) || ! file_exists( $pot_file ) ) |
| 2067 | return false; |
| 2068 | } |
| 2069 | |
| 2070 | // DEBUG |
| 2071 | // system( "cat {$pot_file}" ); |
| 2072 | |
| 2073 | $this->import_to_glotpress_project( $slug, $branch, $pot_file ); |
| 2074 | |
| 2075 | chdir( $old_cwd ); |
| 2076 | return true; |
| 2077 | } |
| 2078 | |
| 2079 | function process_readme_i18n( $path_rel, $branch = 'dev' ) { |
| 2080 | if ( false == ( $export_path = $this->create_export( $path_rel ) ) ) |
| 2081 | return false; |
| 2082 | |
| 2083 | $slug = preg_replace( '|^/?([^/]+)/?.+?$|', '\1', $path_rel ); |
| 2084 | |
| 2085 | if ( empty( $slug ) || empty( $export_path ) || !preg_match( '/^[a-z0-9-]+$/i', $slug ) || !is_dir( $export_path ) ) |
| 2086 | return false; |
| 2087 | |
| 2088 | $old_cwd = getcwd(); |
| 2089 | chdir( $export_path ); |
| 2090 | |
| 2091 | $readme = $this->parse_readme_in( $path_rel ); |
| 2092 | |
| 2093 | if ( !class_exists( 'PO' ) ) |
| 2094 | require_once( __DIR__ . '/i18n-tools/pomo/po.php' ); |
| 2095 | |
| 2096 | $pot = new PO; |
| 2097 | |
| 2098 | foreach ( array( 'name', 'license', 'short_description' ) as $key ) { |
| 2099 | $readme[ $key ] = trim( $readme[ $key ] ) ; |
| 2100 | } |
| 2101 | |
| 2102 | // If empty or "sketchy", get the plugin name form the PHP file's headers |
| 2103 | if ( empty( $readme['name'] ) || 'Plugin Name' == trim( $readme['name'] ) ) { |
| 2104 | // -o in grep will make sure we don't get comments opening delimiters (//, /*) or spaces as part of string |
| 2105 | $name_from_php = trim( shell_exec( 'grep -o "\bPlugin Name:.*" ' . escapeshellarg( $slug ) . '.php' ) ); |
| 2106 | // Remove the header label |
| 2107 | $name_from_php = str_replace( 'Plugin Name:', '', $name_from_php ); |
| 2108 | // Do clean out potential comment closing delimiter (*/) out of string |
| 2109 | $name_from_php = preg_replace( '|^(.+)[\s]+?\*/$|', '\1', $name_from_php ); |
| 2110 | // Use trimmed results as plugin name |
| 2111 | $readme['name'] = trim( $name_from_php ); |
| 2112 | } |
| 2113 | |
| 2114 | if ( !empty( $readme['name'] ) ) { |
| 2115 | $pot->add_entry( new Translation_Entry ( array( |
| 2116 | 'singular' => $readme['name'], |
| 2117 | 'extracted_comments' => 'Plugin/theme name.', |
| 2118 | ) ) ); |
| 2119 | } |
| 2120 | |
| 2121 | if ( !empty( $readme['license'] ) ) { |
| 2122 | $pot->add_entry( new Translation_Entry ( array( |
| 2123 | 'singular' => $readme['license'], |
| 2124 | 'extracted_comments' => 'License.', |
| 2125 | ) ) ); |
| 2126 | } |
| 2127 | |
| 2128 | if ( !empty( $readme['short_description'] ) ) { |
| 2129 | $pot->add_entry( new Translation_Entry ( array( |
| 2130 | 'singular' => $readme['short_description'], |
| 2131 | 'extracted_comments' => 'Short description.', |
| 2132 | ) ) ); |
| 2133 | } |
| 2134 | |
| 2135 | if ( !empty( $readme['screenshots'] ) ) { |
| 2136 | foreach ( $readme['screenshots'] as $sshot_key => $sshot_desc ) { |
| 2137 | $sshot_desc = trim( $sshot_desc ); |
| 2138 | $pot->add_entry( new Translation_Entry ( array( |
| 2139 | 'singular' => $sshot_desc, |
| 2140 | 'extracted_comments' => 'Screenshot description.', |
| 2141 | ) ) ); |
| 2142 | } |
| 2143 | |
| 2144 | } |
| 2145 | |
| 2146 | if ( empty( $readme['sections'] ) ) |
| 2147 | $readme['sections'] = array(); |
| 2148 | |
| 2149 | // Adding remaining content as a section so it's processed by the same loop below |
| 2150 | if ( !empty( $readme['remaining_content'] ) ) |
| 2151 | $readme['sections']['remaining_content'] = $readme['remaining_content']; |
| 2152 | |
| 2153 | $strings = array(); |
| 2154 | |
| 2155 | foreach ( $readme['sections'] as $section_key => $section_text ) { |
| 2156 | if ( 'screenshots' == $section_key ) |
| 2157 | continue; |
| 2158 | |
| 2159 | /* |
| 2160 | * Scanned tags based on block elements found in Automattic_Readme::filter_text() $allowed. |
| 2161 | * Scanning H3/4, li, p and blockquote. Other tags are ignored in strings (a, strong, cite, etc). |
| 2162 | * Processing notes: |
| 2163 | * * Don't normalize/modify original text, will be used as a search pattern in original doc in some use-cases. |
| 2164 | * * Using regexes over XML parsing for performance reasons, could move to the latter for more accuracy. |
| 2165 | */ |
| 2166 | |
| 2167 | if ( 'changelog' !== $section_key ) { // No need to scan non-translatable version headers in changelog |
| 2168 | if ( preg_match_all( '|<h[3-4]+[^>]*>(.+)</h[3-4]+>|', $section_text, $matches ) ) { |
| 2169 | if ( !empty( $matches[1] ) ) { |
| 2170 | foreach ( $matches[1] as $text ) { |
| 2171 | $strings = $this->handle_translator_comment( $strings, $text, "{$section_key} header" ); |
| 2172 | } |
| 2173 | } |
| 2174 | } |
| 2175 | } |
| 2176 | |
| 2177 | if ( preg_match_all( '|<li>(.+)</li>|', $section_text, $matches ) ) { |
| 2178 | if ( !empty( $matches[1] ) ) { |
| 2179 | foreach ( $matches[1] as $text ) { |
| 2180 | $strings = $this->handle_translator_comment( $strings, $text, "{$section_key} list item" ); |
| 2181 | } |
| 2182 | } |
| 2183 | } |
| 2184 | |
| 2185 | if ( preg_match_all( '|<p>(.+)</p>|', $section_text, $matches ) ) { |
| 2186 | if ( !empty( $matches[1] ) ) { |
| 2187 | foreach ( $matches[1] as $text ) { |
| 2188 | $strings = $this->handle_translator_comment( $strings, $text, "{$section_key} paragraph" ); |
| 2189 | } |
| 2190 | } |
| 2191 | } |
| 2192 | |
| 2193 | if ( preg_match_all( '|<blockquote>(.+)</blockquote>|', $section_text, $matches ) ) { |
| 2194 | if ( !empty( $matches[1] ) ) { |
| 2195 | foreach ( $matches[1] as $text ) { |
| 2196 | $strings = $this->handle_translator_comment( $strings, $text, "{$section_key} block quote" ); |
| 2197 | } |
| 2198 | } |
| 2199 | } |
| 2200 | } |
| 2201 | |
| 2202 | foreach ( $strings as $text => $comments ) { |
| 2203 | $pot->add_entry( new Translation_Entry ( array( |
| 2204 | 'singular' => $text, |
| 2205 | 'extracted_comments' => 'Found in ' . implode( $comments, ", " ) . '.', |
| 2206 | ) ) ); |
| 2207 | } |
| 2208 | |
| 2209 | $pot_file = "./{$slug}-readme.pot"; |
| 2210 | $pot->export_to_file( $pot_file ); |
| 2211 | |
| 2212 | // DEBUG |
| 2213 | // system( "cat {$pot_file}" ); |
| 2214 | |
| 2215 | // import to GlotPress, dev or stable |
| 2216 | $this->import_to_glotpress_project( $slug, "{$branch}-readme", $pot_file ); |
| 2217 | |
| 2218 | chdir( $old_cwd ); |
| 2219 | return true; |
| 2220 | } |
| 2221 | |
| 2222 | function handle_translator_comment( $array, $key, $val ) { |
| 2223 | $val = trim( preg_replace( '/[^a-z0-9]/i', ' ', $val ) ); // cleanup key names for display |
| 2224 | if ( empty( $array[ $key ] ) ) { |
| 2225 | $array[ $key ] = array( $val ); |
| 2226 | } else if ( !in_array( $val, $array[ $key ] ) ) { |
| 2227 | $array[ $key ][] = $val; |
| 2228 | } |
| 2229 | return $array; |
| 2230 | } |
| 2231 | |
| 2232 | function import_to_glotpress_project( $project, $branch, $file ) { |
| 2233 | $cmd = 'php ' . __DIR__ . '/../../../translate/glotpress/scripts/import-originals.php -o po -p ' . escapeshellarg( "wp-plugins/{$project }/{$branch}" ) . ' -f ' . escapeshellarg( $file ); |
| 2234 | // DEBUG |
| 2235 | // var_dump( $cmd ); |
| 2236 | system( $cmd ); |
| 2237 | // Note: this will nly work if the GlotPress project/sub-projects exist. To be improved, or must insure they are. |
| 2238 | } |
| 2239 | |