All skills
simota avatar

/polyglot

@35ffd55
by shingo imotasimota/agent-skills85 stars
15

Implementing i18n and l10n: extracts hardcoded strings to t() functions, integrates Intl API for date/currency/number formatting, manages translation keys, and adds RTL layout support.

Use this Skill: https://skilld.dev/gh/simota/agent-skills/polyglot

This session only. Nothing lands on disk.

referencertl-support.md

≈1.7k tokens on demand. Your agent reads this file only when SKILL.md points to it.

RTL (Right-to-Left) Support Guide

Comprehensive guide for supporting RTL languages (Arabic, Hebrew, Farsi, Urdu).


CSS Logical Properties

The foundation of RTL support. Logical properties automatically flip for RTL layouts.

Physical to Logical Mapping

/* BAD: Physical properties (don't auto-flip) */
.card {
  margin-left: 16px;
  padding-right: 24px;
  text-align: left;
  border-left: 2px solid blue;
}

/* GOOD: Logical properties (auto-flip for RTL) */
.card {
  margin-inline-start: 16px;
  padding-inline-end: 24px;
  text-align: start;
  border-inline-start: 2px solid blue;
}

Complete Mapping Reference

Physical (LTR only) Logical (LTR + RTL)
margin-left margin-inline-start
margin-right margin-inline-end
padding-left padding-inline-start
padding-right padding-inline-end
left inset-inline-start
right inset-inline-end
text-align: left text-align: start
text-align: right text-align: end
border-left border-inline-start
border-right border-inline-end
float: left float: inline-start
float: right float: inline-end
margin-top margin-block-start
margin-bottom margin-block-end

Dynamic dir Attribute

React

import { useTranslation } from 'react-i18next';

function App() {
  const { i18n } = useTranslation();
  const dir = ['ar', 'he', 'fa', 'ur'].includes(i18n.language) ? 'rtl' : 'ltr';

  return (
    <div dir={dir} lang={i18n.language}>
      {/* App content */}
    </div>
  );
}

// Or set on html element
useEffect(() => {
  const dir = ['ar', 'he', 'fa', 'ur'].includes(i18n.language) ? 'rtl' : 'ltr';
  document.documentElement.dir = dir;
  document.documentElement.lang = i18n.language;
}, [i18n.language]);

Icon and Layout Flipping

Icons That Should Flip

/* Directional icons: flip in RTL */
.icon-arrow,
.icon-chevron,
.icon-back {
  [dir="rtl"] & {
    transform: scaleX(-1);
  }
}

/* Non-directional icons: keep as-is */
.icon-checkmark,
.icon-clock,
.icon-play {
  /* No RTL override needed */
}

React Directional Icon Component

function DirectionalIcon({ icon, flip = true }: { icon: string; flip?: boolean }) {
  const { i18n } = useTranslation();
  const isRTL = ['ar', 'he', 'fa', 'ur'].includes(i18n.language);

  return (
    <span
      className={icon}
      style={{ transform: flip && isRTL ? 'scaleX(-1)' : undefined }}
    />
  );
}

Bidirectional Text (Bidi)

Embedding LTR in RTL Context

// Phone numbers, emails, URLs should always be LTR
function PhoneNumber({ number }: { number: string }) {
  return (
    <span dir="ltr" style={{ unicodeBidi: 'embed' }}>
      {number}
    </span>
  );
}

Embedding RTL in LTR Context

function ArabicName({ name }: { name: string }) {
  return (
    <span dir="rtl" style={{ unicodeBidi: 'embed' }}>
      {name}
    </span>
  );
}

Isolating User Content

function UserContent({ content, dir }: { content: string; dir: 'ltr' | 'rtl' }) {
  return (
    <span dir={dir} style={{ unicodeBidi: 'isolate' }}>
      {content}
    </span>
  );
}

RTL Testing Checklist

Layout

  • Text alignment flips correctly (start/end)
  • Margins and padding flip correctly
  • Flexbox/Grid items reorder correctly
  • Scroll direction is correct

Icons

  • Directional icons (arrows, chevrons) flip
  • Non-directional icons remain unchanged
  • Icon + text spacing is correct

Forms

  • Input text direction is correct
  • Label alignment is correct
  • Error messages align correctly
  • Placeholder text direction is correct

Navigation

  • Back/forward buttons flip
  • Breadcrumbs read right-to-left
  • Menu items align correctly
  • Dropdown menus open in correct direction

Content

  • Mixed LTR/RTL content displays correctly
  • Phone numbers display LTR
  • Email addresses display LTR
  • URLs display LTR

Testing Tools

  • Chrome DevTools: Force RTL with document.dir = 'rtl'
  • Browser extensions: RTL toggle extensions
  • Pseudo-locale: Use RTL test locale

I18n Test Automation

Pseudo-Locale Generation

Generate pseudo-localized strings that expand text by 30-40% and replace characters with accented variants to detect layout overflow before real translations arrive.

// scripts/generate-pseudo-locale.ts
const CHAR_MAP: Record<string, string> = {
  a: 'á', b: 'b̈', c: 'ć', d: 'ď', e: 'é', f: 'f̈',
  g: 'ĝ', h: 'ḧ', i: 'í', j: 'ĵ', k: 'ǩ', l: 'ĺ',
  m: 'm̈', n: 'ń', o: 'ó', p: 'ṗ', q: 'q̈', r: 'ŕ',
  s: 'ś', t: 'ţ', u: 'ú', v: 'v̈', w: 'ŵ', x: 'x̃',
  y: 'ý', z: 'ź',
};

function toPseudoLocale(str: string): string {
  const withoutVars = str.replace(/(\{\{?\w+\}?\})/g, '\0$1\0');
  const parts = withoutVars.split('\0');
  const converted = parts
    .map((part) =>
      part.startsWith('{')
        ? part
        : part.split('').map((c) => CHAR_MAP[c.toLowerCase()] ?? c).join('')
    )
    .join('');
  return `[!! ${converted} !!]`;
}

toPseudoLocale('Hello, {{name}}!');
// → "[!! Ḧéĺĺó, {{name}}! !!]"

Playwright Screenshot Tests

// tests/i18n/screenshot.spec.ts
import { test, expect } from '@playwright/test';

const LOCALES = ['en', 'ja', 'ar', 'pseudo'] as const;

for (const locale of LOCALES) {
  test(`layout renders correctly for ${locale}`, async ({ page }) => {
    await page.goto(`/${locale}/dashboard`);
    await expect(page).toHaveScreenshot(`dashboard-${locale}.png`, {
      fullPage: true,
      threshold: 0.05,
    });
  });
}

Translation Coverage Test

// tests/i18n/coverage.test.ts
import en from '../../messages/en.json';
import ja from '../../messages/ja.json';

function flattenKeys(obj: object, prefix = ''): string[] {
  return Object.entries(obj).flatMap(([k, v]) =>
    typeof v === 'object' && v !== null
      ? flattenKeys(v, `${prefix}${k}.`)
      : [`${prefix}${k}`]
  );
}

describe('translation coverage', () => {
  const enKeys = flattenKeys(en);
  const jaKeys = flattenKeys(ja);

  test('ja has all en keys', () => {
    const missing = enKeys.filter((k) => !jaKeys.includes(k));
    expect(missing).toEqual([]);
  });

  test('ja has no extra keys', () => {
    const extra = jaKeys.filter((k) => !enKeys.includes(k));
    expect(extra).toEqual([]);
  });
});

RTL Layout Verification

// tests/i18n/rtl.spec.ts
test('Arabic layout is RTL', async ({ page }) => {
  await page.goto('/ar/dashboard');

  const dir = await page.getAttribute('html', 'dir');
  expect(dir).toBe('rtl');

  const card = page.locator('.card').first();
  const marginStart = await card.evaluate((el) =>
    getComputedStyle(el).marginInlineStart
  );
  expect(parseFloat(marginStart)).toBeGreaterThan(0);
});

Source: SKILL.md on GitHub

No alerts13d5 checks · Risk SAFE
  • Gen Agent Trust Hub13d

    The 'Polyglot' skill provides comprehensive guidelines and code patterns for internationalization and localization. It includes support for string extraction, Intl API usage, ICU Message Format, and RTL layouts. The skill follows industry standards and integrates with established translation management services without introducing security risks.

  • Socket13d

    No alerts

  • Snyk13d

    Risk: LOW · No issues

  • Runlayer6mo

    5 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at 35ffd55. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated 2 weeks ago

README badge

README badge for simota/agent-skills/polyglot