array( 'class' => true, 'width' => true, 'height' => true, 'viewbox' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'aria-hidden' => true, 'focusable' => true, ), 'path' => array( 'd' => true, 'fill' => true, 'stroke' => true, 'stroke-width' => true, 'stroke-linecap' => true, 'stroke-linejoin' => true, ), 'circle' => array( 'cx' => true, 'cy' => true, 'r' => true, 'fill' => true, 'stroke' => true, ), ); /** * Prints the screen header: breadcrumb, title, optional description and action buttons. * * On a screen of a group, the group's header above is the page's title and says where it is, see * Menu::renderScreen(). This header is then the header of the screen's part of the page: its * title one level down, and its path only on a screen deeper than the group's list, such as one * menu of Menus, where the crumb names the screen of the list it belongs to. * * @param array $args Keys: crumb (label after "SwiftLing"), title, description (optional), * actions (optional list of array( label, variant, url ) buttons; url is optional). */ public static function screenHeader( array $args ): void { $args = wp_parse_args( $args, array( 'crumb' => '', 'title' => '', 'description' => '', 'actions' => array(), ) ); $group = Menu::renderingGroup(); $section = '' !== $group; ?>

SwiftLing

'M4 5h8M8 5V3.5M5.5 11c2.6-1.3 4.6-3.6 5.3-6M6.8 7.8c.9 1.7 2.3 3 4 3.7M13 20l3.6-8.5L20.2 20M14.3 17.2h4.6', 'ai' => 'M11 3.5l1.9 5.1 5.1 1.9-5.1 1.9L11 17.5l-1.9-5.1L4 10.5l5.1-1.9zM18 14.5l.8 2 2 .8-2 .8-.8 2-.8-2-2-.8 2-.8z', ); $kind = isset( $paths[ $kind ] ) ? $kind : 'machine'; $tints = array( 'blue', 'teal', 'violet', 'amber', 'green' ); $tint = '' !== $id ? $tints[ abs( crc32( $id ) ) % count( $tints ) ] : ( 'ai' === $kind ? 'violet' : 'blue' ); printf( '', esc_attr( $kind ), esc_attr( $tint ), esc_attr( $paths[ $kind ] ) ); } /** * Prints a notice: a tinted card with a round sticker, a bold first line, its text and the * buttons of where it leads. Every notice of the SwiftLing screens comes from here, so they all * look the same; a test fails when one is written by hand. * * Kinds: success (a tick), info (a tip, with the SwiftLing bird), warning (!) and error (×). An * error is announced at once, the others when the reader gets to them. * * @param string $kind success, info, warning or error; anything else is info. * @param array $args Keys: title (the bold first line), text (plain text), html * (text with links or emphasis, instead of text), link (array( label, url ), * a link after the text), actions (list of array( label, url ) buttons), * compact (true for a notice inside a card) and class (more classes). */ public static function notice( string $kind, array $args ): void { $args = wp_parse_args( $args, array( 'title' => '', 'text' => '', 'html' => '', 'link' => array(), 'actions' => array(), 'compact' => false, 'class' => '', ) ); // Links to screens the user may not open are left out. if ( $args['link'] && ! self::opens( (string) $args['link'][1] ) ) { $args['link'] = array(); } $args['actions'] = array_values( array_filter( (array) $args['actions'], static function ( $action ) { return self::opens( (string) $action[1] ); } ) ); $kind = in_array( $kind, array( 'success', 'info', 'warning', 'error' ), true ) ? $kind : 'info'; $classes = trim( 'swiftling-notice is-' . $kind . ( $args['compact'] ? ' is-compact' : '' ) . ' ' . $args['class'] ); $icons = array( 'success' => '', 'info' => '', 'warning' => '', 'error' => '', ); $allowed = array( 'a' => array( 'href' => true ), 'strong' => array(), 'em' => array(), 'code' => array(), ); ?>

', esc_attr( $class ), esc_url( $url ) ); } else { printf( ''; } /** * Prints the tools of a long list in the translation editor: which of its entries show, a search, * and the pages of the list. Each is a link or a form that reloads the editor. * * @param array $args Keys: subject (key of the editor's subject), lang (language * slug), shows (optional, show => array( label, count )), show (the one shown), * search (words searched for), placeholder (of the search field), page, pages, * per_page, shown (entries found) and keep (optional, other values of the * editor's address every link and the search keep, such as the page whose * comments show). */ public static function listFilters( array $args ): void { $args = wp_parse_args( $args, array( 'shows' => array(), 'show' => '', 'keep' => array(), 'search' => '', 'placeholder' => __( 'Search texts', 'swiftling' ), 'page' => 1, 'pages' => 1, 'per_page' => 1, 'shown' => 0, ) ); $base = swiftling_editor_url( $args['subject'], $args['lang'] ); $link = static function ( array $more ) use ( $base, $args ) { $query = array_merge( $args['keep'], array( 'show' => $args['show'], 'find' => $args['search'], ), $more ); return add_query_arg( array_map( 'rawurlencode', array_filter( array_map( 'strval', $query ) ) ), $base ); }; ?>
$entry ) : ?> >
1 ) : ?> */ public static function partLabels(): array { return array( 'heading' => __( 'Heading', 'swiftling' ), 'paragraph' => __( 'Paragraph', 'swiftling' ), 'list' => __( 'List', 'swiftling' ), 'button' => __( 'Button', 'swiftling' ), 'link' => __( 'Link', 'swiftling' ), 'alt' => __( 'Alt text', 'swiftling' ), 'caption' => __( 'Caption', 'swiftling' ), 'label' => __( 'Label', 'swiftling' ), 'quote' => __( 'Quote', 'swiftling' ), 'seo_title' => __( 'SEO title', 'swiftling' ), 'meta_description' => __( 'Meta description', 'swiftling' ), 'social_title' => __( 'Title when shared', 'swiftling' ), 'social_description' => __( 'Description when shared', 'swiftling' ), 'structured' => __( 'Structured data', 'swiftling' ), 'text' => __( 'Text', 'swiftling' ), 'url' => __( 'Link address', 'swiftling' ), 'image' => __( 'Image', 'swiftling' ), 'video' => __( 'Video or audio', 'swiftling' ), 'code' => __( 'Code', 'swiftling' ), 'name' => __( 'Name', 'swiftling' ), 'description' => __( 'Description', 'swiftling' ), ); } /** * Labels for the parts of a page a text can sit in. * * @return array */ public static function regionLabels(): array { return array( 'header' => __( 'Header', 'swiftling' ), 'navigation' => __( 'Navigation', 'swiftling' ), 'footer' => __( 'Footer', 'swiftling' ), 'sidebar' => __( 'Sidebar', 'swiftling' ), 'head' => __( 'Search results', 'swiftling' ), ); } /** * Registers the stylesheet of the state icons and language chips, which the SwiftLing screens * and the Translations column of the admin lists share. * * @return string Its handle. */ public static function registerStatesStyle(): string { wp_register_style( 'swiftling-states', SWIFTLING_URL . 'assets/css/states.css', array(), SWIFTLING_VERSION ); return 'swiftling-states'; } /** * Labels for translation and connection states. * * @return array */ public static function statusLabels(): array { return array( 'reviewed' => __( 'Reviewed', 'swiftling' ), 'machine' => __( 'Machine translated', 'swiftling' ), 'official' => __( 'Translated by WordPress, the theme or the plugin', 'swiftling' ), 'outdated' => __( 'Outdated', 'swiftling' ), 'partial' => __( 'Partly translated', 'swiftling' ), 'missing' => __( 'Not translated', 'swiftling' ), 'duplicate' => __( 'Keeps the original', 'swiftling' ), 'published' => __( 'Published', 'swiftling' ), 'hidden' => __( 'Hidden', 'swiftling' ), 'working' => __( 'Working', 'swiftling' ), 'not-connected' => __( 'Not connected', 'swiftling' ), 'failed' => __( 'Key does not work', 'swiftling' ), 'unpaid' => __( 'Key works · needs credit', 'swiftling' ), 'untested' => __( 'Not checked yet', 'swiftling' ), ); } /** * Prints a coloured pill for a state, e.g. "Reviewed". * * @param string $status Key from self::statusLabels(). * @param string $label Optional text instead of the default label. */ public static function badge( string $status, string $label = '' ): void { $labels = self::statusLabels(); printf( '%s', esc_attr( $status ), esc_html( '' !== $label ? $label : $labels[ $status ] ) ); } /** * Prints a round status icon with a text alternative for screen readers. * * @param string $status One of reviewed, machine, official, outdated, partial, missing. */ public static function statusIcon( string $status ): void { $labels = self::statusLabels(); printf( '%2$s', esc_attr( $status ), esc_attr( $labels[ $status ] ) ); } /** * Prints the choice of a language's flag: the flag now shown, a list of countries with automatic * and no flag, and an image of the site's own. The Languages and the Switcher screen print the * same fields, flags[locale][country] and flags[locale][image], which Flags::sanitize() saves, * and assets/js/flag-choice.js keeps the picture right. * * @param string $locale Locale of the language, e.g. "de_DE". * @param string $name Name of the language, for screen readers. */ public static function flagChoice( string $locale, string $name ): void { $countries = Flags::countries(); $choice = Flags::choice( $locale ); $automatic = Flags::automatic( $locale ); $field = 'flags[' . $locale . ']'; $id = 'swiftling-flag-' . sanitize_html_class( $locale ); $current = Flags::url( $locale ); ?> > %s', $is_source ? ' is-source' : '', esc_html( $code ) ); } /** * Prints the notice that language folders are the addresses of pages of the site, which then * show the language instead: after the folders or a language's address change. * * @param string[] $taken Language folders, e.g. array( 'de' ). */ public static function takenFolders( array $taken ): void { if ( ! $taken ) { return; } $list = implode( ', ', array_map( static function ( string $slug ): string { return '/' . $slug . '/'; }, $taken ) ); self::notice( 'warning', array( /* translators: %s: language folders, e.g. /de/. */ 'text' => sprintf( _n( 'A page of this site already uses the address %s. The language now shows there instead, so change the slug of that page or the address of the language to open the page again.', 'Pages of this site already use the addresses %s. The languages now show there instead, so change the slugs of those pages or the addresses of the languages to open the pages again.', count( $taken ), 'swiftling' ), $list ), ) ); } /** * Prints a card that says a screen has nothing to show yet, and where to go instead. * * @param string $title What is missing. * @param string $text Why, and what to do. * @param array $action Optional array( label, url ) for a button. */ public static function emptyState( string $title, string $text, array $action = array() ): void { ?>