Dictionariespage - Source Excerpt 01
Summary
This source excerpt preserves a bounded section of Antichrist.net/wp-content/plugins/uaix-locale-router/src/Admin/DictionariesPage.php so readers can inspect the evidence without opening the full source file.
**Source path:** Antichrist.net/wp-content/plugins/uaix-locale-router/src/Admin/DictionariesPage.php
<?php
namespace UAIXLocaleRouter\Admin;
use UAIXLocaleRouter\DictionaryRepository;
use UAIXLocaleRouter\DictionaryValidator;
use UAIXLocaleRouter\LocaleRepository;
use UAIXLocaleRouter\LocaleValidator;
use UAIXLocaleRouter\Plugin;
use UAIXLocaleRouter\TranslationCatalog;
use UAIXLocaleRouter\Support\Html;
use UAIXLocaleRouter\Support\Json;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
final class DictionariesPage {
/**
* Render the dictionaries page.
*
* @return void
*/
public static function render_page() {
$manifest = DictionaryRepository::dictionary_manifest();
$export_locales = self::export_locale_options();
?>
<div class="wrap">
<h1>Dictionaries</h1>
<div class="uaixlr-grid">
<div class="uaixlr-card">
<h2>Upload Dictionary</h2>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>" enctype="multipart/form-data">
<?php wp_nonce_field( 'uaixlr_upload_dictionary' ); ?>
<input type="hidden" name="action" value="uaixlr_upload_dictionary" />
<p>
<label for="uaixlr_dictionary_file">Dictionary JSON file</label><br />
<input type="file" id="uaixlr_dictionary_file" name="dictionary_file" accept=".json,application/json" required />
</p>
<?php submit_button( 'Upload Dictionary', 'primary', 'submit', false ); ?>
</form>
</div>
<div class="uaixlr-card">
<h2>Template Dictionary</h2>
<p>Download the full source-text template for the current site inventory. The file uses English source text as the key and empty values for translation.</p>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<?php wp_nonce_field( 'uaixlr_download_template' ); ?>
<input type="hidden" name="action" value="uaixlr_download_template" />
<?php submit_button( 'Download Template JSON', 'secondary', 'submit', false ); ?>
</form>
</div>
<div class="uaixlr-card">
<h2>Locale Dictionary Export</h2>
<p>Download one complete translator-facing JSON file per locale. English repeats the source text. Other locales use the same English keys with translated values.</p>
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
<?php wp_nonce_field( 'uaixlr_download_dictionary' ); ?>
<input type="hidden" name="action" value="uaixlr_download_dictionary" />
<p>
<label for="uaixlr_export_locale">Locale</label><br />
<select id="uaixlr_export_locale" name="locale_tag">
<?php foreach ( $export_locales as $value => $label ) : ?>
<option value="<?php echo esc_attr( $value ); ?>"><?php echo esc_html( $label ); ?></option>
<?php endforeach; ?>
</select>
</p>
<?php submit_button( 'Download Locale JSON', 'secondary', 'submit', false ); ?>
</form>
</div>
</div>
<div class="uaixlr-card uaixlr-card-wide">
<h2>Dictionary Manifest</h2>
<?php if ( empty( $manifest ) ) : ?>
<p>No dictionaries have been uploaded yet.</p>
<?php else : ?>
<table class="widefat striped">
<thead>
<tr>
<th>Locale</th>
<th>Status</th>
<th>Quality</th>
<th>Issues</th>
<th>Uploaded</th>
<th>Hash</th>
</tr>
</thead>
<tbody>
<?php foreach ( $manifest as $locale_tag => $entry ) : ?>
<tr>
<td><code><?php echo esc_html( $locale_tag ); ?></code></td>
<td><?php echo wp_kses_post( Html::badge( isset( $entry['lastValidationStatus'] ) ? $entry['lastValidationStatus'] : 'draft', isset( $entry['lastValidationStatus'] ) && 'approved' === $entry['lastValidationStatus'] ? 'success' : ( isset( $entry['lastValidationStatus'] ) && 'rejected' === $entry['lastValidationStatus'] ? 'error' : 'warning' ) ) ); ?></td>
<td><?php echo wp_kses_post( Html::badge( isset( $entry['qualityStatus'] ) ? $entry['qualityStatus'] : 'draft', 'neutral' ) ); ?></td>
<td><?php echo esc_html( isset( $entry['issueCount'] ) ? (int) $entry['issueCount'] : 0 ); ?></td>
<td><?php echo esc_html( isset( $entry['uploadedAt'] ) ? $entry['uploadedAt'] : '-' ); ?></td>
<td><code><?php echo esc_html( isset( $entry['fileHash'] ) ? substr( $entry['fileHash'], 0, 12 ) : '-' ); ?></code></td>
</tr>
<?php if ( ! empty( $entry['lastIssues'] ) && is_array( $entry['lastIssues'] ) ) : ?>
<tr>
<td colspan="6"><?php echo wp_kses_post( Html::issues_list( $entry['lastIssues'] ) ); ?></td>
</tr>
<?php endif; ?>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
</div>
</div>
<?php
}
/**
* Handle secure dictionary upload.
*
* @return void
*/
public static function handle_upload() {
self::assert_permissions( 'uaixlr_upload_dictionary' );
if ( empty( $_FILES['dictionary_file']['tmp_name'] ) || empty( $_FILES['dictionary_file']['name'] ) ) {
Plugin::set_flash_notice( 'Please choose a dictionary JSON file to upload.', 'error' );
wp_safe_redirect( AdminPages::page_url( 'uaixlr-dictionaries' ) );
exit;
}
$file_name = sanitize_file_name( wp_unslash( $_FILES['dictionary_file']['name'] ) );
$tmp_name = $_FILES['dictionary_file']['tmp_name'];
$file_size = isset( $_FILES['dictionary_file']['size'] ) ? absint( $_FILES['dictionary_file']['size'] ) : 0;
if ( ! is_uploaded_file( $tmp_name ) ) {
Plugin::set_flash_notice( 'The uploaded file could not be verified.', 'error' );
wp_safe_redirect( AdminPages::page_url( 'uaixlr-dictionaries' ) );
exit;
}
if ( '.json' !== strtolower( substr( $file_name, -5 ) ) ) {
Plugin::set_flash_notice( 'Only JSON dictionary files are supported.', 'error' );
wp_safe_redirect( AdminPages::page_url( 'uaixlr-dictionaries' ) );
exit;
}
if ( $file_size > DictionaryValidator::MAX_JSON_BYTES ) {
Plugin::set_flash_notice( 'The uploaded dictionary is larger than the allowed 1 MB limit.', 'error' );
wp_safe_redirect( AdminPages::page_url( 'uaixlr-dictionaries' ) );
exit;
}