Commands.php
3 years ago
ExtractCommand.php
1 year ago
FetchCommand.php
1 year ago
SyncCommand.php
1 year ago
Utils.php
1 year ago
SyncCommand.php
181 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Called from Loco_cli_Commands::sync |
| 4 | */ |
| 5 | abstract class Loco_cli_SyncCommand { |
| 6 | |
| 7 | |
| 8 | /** |
| 9 | * @param Loco_package_Project[] $projects project filter |
| 10 | * @param Loco_Locale[] $locales locale filter |
| 11 | * @param bool $noop whether dry run |
| 12 | * @param bool $force whether to always update |
| 13 | */ |
| 14 | public static function run( array $projects, array $locales, $noop = true, $force = false ){ |
| 15 | |
| 16 | if( $force && $noop ){ |
| 17 | throw new Loco_error_Exception('--force makes no sense with --noop'); |
| 18 | } |
| 19 | |
| 20 | $content_dir = loco_constant('WP_CONTENT_DIR'); |
| 21 | $wp_locales = new Loco_api_WordPressTranslations; |
| 22 | |
| 23 | // track total number of PO files synced, plus MO and JSON files compiled |
| 24 | $updated = 0; |
| 25 | $compiled = 0; |
| 26 | |
| 27 | foreach( $projects as $project ){ |
| 28 | $id = rtrim( $project->getId(), '.' ); |
| 29 | $base_dir = $project->getBundle()->getDirectoryPath(); |
| 30 | WP_CLI::log( sprintf('Syncing "%s" (%s)',$project->getName(),$id) ); |
| 31 | // Check if project has POT, which will be used as default template unless PO overrides |
| 32 | $pot = null; |
| 33 | $potfile = $project->getPot(); |
| 34 | if( $potfile && $potfile->exists() ){ |
| 35 | Loco_cli_Utils::debug('Parsing template: %s',$potfile->getRelativePath($content_dir)); |
| 36 | try { |
| 37 | $pot = Loco_gettext_Data::fromSource( $potfile->getContents() ); |
| 38 | } |
| 39 | catch( Loco_error_ParseException $e ){ |
| 40 | WP_CLI::error( $e->getMessage().' in '.$potfile->getRelativePath($content_dir), false ); |
| 41 | $potfile = null; |
| 42 | } |
| 43 | } |
| 44 | /* @var Loco_fs_LocaleFile $pofile */ |
| 45 | $pofiles = $project->findLocaleFiles('po'); |
| 46 | foreach( $pofiles as $pofile ){ |
| 47 | $locale = $pofile->getLocale(); |
| 48 | $tag = (string) $locale; |
| 49 | if( $locales && ! array_key_exists($tag,$locales) ){ |
| 50 | continue; |
| 51 | } |
| 52 | // Preempt write errors and print useful file mode info |
| 53 | $mofile = $pofile->cloneExtension('mo'); |
| 54 | if( ! $pofile->writable() || $mofile->locked() ){ |
| 55 | WP_CLI::warning('Skipping unwritable: '.self::fname($pofile) ); |
| 56 | Loco_cli_Utils::tabulateFiles( $pofile->getParent(), $pofile, $mofile ); |
| 57 | continue; |
| 58 | } |
| 59 | // Parsing candidate PO file (definitions) |
| 60 | Loco_cli_Utils::debug('Parsing PO: %s',$pofile->getRelativePath($content_dir)); |
| 61 | try { |
| 62 | $def = Loco_gettext_Data::fromSource( $pofile->getContents() ); |
| 63 | } |
| 64 | catch( Loco_error_ParseException $e ){ |
| 65 | WP_CLI::error( $e->getMessage().' in '.$pofile->getRelativePath($content_dir), false ); |
| 66 | continue; |
| 67 | } |
| 68 | // Check if PO defines alternative template (reference) |
| 69 | $ref = $pot; |
| 70 | $head = $def->getHeaders(); |
| 71 | $opts = new Loco_gettext_SyncOptions($head); |
| 72 | $translate = $opts->mergeMsgstr(); |
| 73 | if( $opts->hasTemplate() ){ |
| 74 | $ref = null; |
| 75 | $potfile = $opts->getTemplate(); |
| 76 | $potfile->normalize( $base_dir ); |
| 77 | if( $potfile->exists() ){ |
| 78 | try { |
| 79 | Loco_cli_Utils::debug('> Parsing alternative template: %s',$potfile->getRelativePath($content_dir) ); |
| 80 | $ref = Loco_gettext_Data::fromSource( $potfile->getContents() ); |
| 81 | } |
| 82 | catch( Loco_error_ParseException $e ){ |
| 83 | WP_CLI::error( $e->getMessage().' in '.$potfile->getRelativePath($content_dir), false ); |
| 84 | } |
| 85 | } |
| 86 | else { |
| 87 | Loco_cli_Utils::debug('Template not found (%s)', $potfile->basename() ); |
| 88 | } |
| 89 | } |
| 90 | if( ! $ref ){ |
| 91 | WP_CLI::warning( sprintf('Skipping %s; no valid translation template',$pofile->getRelativePath($content_dir) ) ); |
| 92 | continue; |
| 93 | } |
| 94 | // Perform merge if we have a reference file |
| 95 | Loco_cli_Utils::debug('Merging %s <- %s', $pofile->basename(), $potfile->basename() ); |
| 96 | $matcher = new Loco_gettext_Matcher($project); |
| 97 | $matcher->loadRefs($ref,$translate ); |
| 98 | // Merge jsons if configured and available |
| 99 | if( $opts->mergeJson() ){ |
| 100 | $siblings = new Loco_fs_Siblings( $potfile->cloneBasename( $pofile->basename() ) ); |
| 101 | $jsons = $siblings->getJsons( $project->getDomain()->getName() ); |
| 102 | $njson = $matcher->loadJsons($jsons); |
| 103 | Loco_cli_Utils::debug('> merged %u json files', $njson ); |
| 104 | } |
| 105 | // Get fuzzy matching tolerance from plugin settings, can be set temporarily in command line |
| 106 | $fuzziness = Loco_data_Settings::get()->fuzziness; |
| 107 | $matcher->setFuzziness( (string) $fuzziness ); |
| 108 | // update matches sources, deferring unmatched for deferred fuzzy match |
| 109 | $po = clone $def; |
| 110 | $po->clear(); |
| 111 | $nvalid = count( $matcher->mergeValid($def,$po) ); |
| 112 | $nfuzzy = count( $matcher->mergeFuzzy($po) ); |
| 113 | $nadded = count( $matcher->mergeAdded($po) ); |
| 114 | $ndropped = count( $matcher->redundant() ); |
| 115 | // TODO Support --previous to keep old strings, or at least comment them out as #| msgid..... |
| 116 | if( $nfuzzy || $nadded || $ndropped ){ |
| 117 | Loco_cli_Utils::debug('> unchanged:%u added:%u fuzzy:%u dropped:%u', $nvalid, $nadded, $nfuzzy, $ndropped ); |
| 118 | } |
| 119 | else { |
| 120 | Loco_cli_Utils::debug('> %u identical sources',$nvalid); |
| 121 | } |
| 122 | // File is synced, but may be identical |
| 123 | $po->sort(); |
| 124 | if( ! $force && $po->equal($def) ){ |
| 125 | WP_CLI::log( sprintf('No update required for %s', self::fname($pofile) ) ); |
| 126 | continue; |
| 127 | } |
| 128 | if( $noop ){ |
| 129 | WP_CLI::success( sprintf('**DRY RUN** would update %s', self::fname($pofile) ) ); |
| 130 | continue; |
| 131 | } |
| 132 | try { |
| 133 | $locale->ensureName($wp_locales); |
| 134 | $po->localize($locale); |
| 135 | $compiler = new Loco_gettext_Compiler($pofile); |
| 136 | $bytes = $compiler->writePo($po); |
| 137 | Loco_cli_Utils::debug('+ %u bytes written to %s',$bytes, $pofile->basename()); |
| 138 | $updated++; |
| 139 | // compile MO |
| 140 | $bytes = $compiler->writeMo($po); |
| 141 | if( $bytes ){ |
| 142 | Loco_cli_Utils::debug('+ %u bytes written to %s',$bytes, $mofile->basename()); |
| 143 | $compiled++; |
| 144 | } |
| 145 | // Done PO/MO pair, now generate JSON fragments as applicable |
| 146 | $jsons = $compiler->writeJson($project,$po); |
| 147 | foreach( $jsons as $file ){ |
| 148 | $compiled++; |
| 149 | $param = new Loco_mvc_FileParams([],$file); |
| 150 | Loco_cli_Utils::debug('+ %u bytes written to %s',$param->size,$param->name); |
| 151 | } |
| 152 | // Done compile of this set |
| 153 | Loco_error_AdminNotices::get()->flush(); |
| 154 | WP_CLI::success( sprintf('Updated %s', self::fname($pofile) ) ); |
| 155 | } |
| 156 | catch( Loco_error_WriteException $e ){ |
| 157 | WP_CLI::error( $e->getMessage(), false ); |
| 158 | } |
| 159 | } |
| 160 | } |
| 161 | // sync summary |
| 162 | if( 0 === $updated ){ |
| 163 | WP_CLI::log('Nothing updated'); |
| 164 | } |
| 165 | else { |
| 166 | WP_CLI::success( sprintf('%u PO files synced, %u files compiled',$updated,$compiled) ); |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | |
| 171 | /** |
| 172 | * Debug file name showing directory location |
| 173 | * @param Loco_fs_File |
| 174 | * @return string |
| 175 | */ |
| 176 | private static function fname( Loco_fs_File $file ){ |
| 177 | $dir = new Loco_fs_LocaleDirectory( $file->dirname() ); |
| 178 | return $file->filename().' ('.$dir->getTypeLabel( $dir->getTypeId() ).')'; |
| 179 | } |
| 180 | |
| 181 | } |